Compare commits

..

46 Commits
1.2.2 ... 1.2.6

Author SHA1 Message Date
Michael Scire
173d5c2d3a docs: add changelog for 1.2.6 2022-01-19 16:41:20 -08:00
Michael Scire
52c1cef064 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "0d161b858"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "0d161b858"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2022-01-19 16:29:16 -08:00
Michael Scire
4efa5d7dd0 ams: first attempt at 13.2.1 support 2022-01-19 16:28:32 -08:00
Michael Scire
275da52305 kern: add missing debug mode check to WriteDebugProcessMemory 2022-01-03 08:59:27 -08:00
Michael Scire
30fac905af ams: deduplicate static initialization logic 2021-12-13 13:07:03 -08:00
Michael Scire
78f7218c4f kern: fix vi devicename missing in HsSupported device list 2021-12-10 18:58:18 -08:00
Michael Scire
09c6aa29dd sf/cmif: optimize dispatch table walk to use binary search over linear search 2021-12-06 13:13:46 -08:00
Michael Scire
1019bc54e6 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "c4d0335b7"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "c4d0335b7"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-11-30 16:53:56 -08:00
Michael Scire
96631d8225 bump version to 1.2.5, fix enum for 13.2.0 2021-11-30 16:53:32 -08:00
Michael Scire
d32dd0f04a git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "b670c079f"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "b670c079f"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-11-30 16:35:09 -08:00
Michael Scire
ddfc16731f ams/hos: add enum support for 13.2.0 2021-11-30 16:22:57 -08:00
Pablo Curiel
26714c7f3b ldr_embedded_usb_patches: add support for HOS 13.0.0 and 13.1.0 + remove superfluous comments. (#1718) 2021-11-26 00:39:22 -07:00
Michael Scire
767e702a70 kern: correct TotalUserPhysicalMemorySize (closes #1710) 2021-11-16 23:18:47 -08:00
Michael Scire
9d5e652fbd kern: be a little more consistent about pragma GCC location 2021-11-16 11:25:57 -08:00
Michael Scire
1d39e06f32 test/svc: add test for thread create 2021-11-08 13:29:00 -08:00
Michael Scire
21b7884653 test/svc: add test for thread pinning/SynchronizePreemptionState 2021-11-08 13:05:06 -08:00
Michael Scire
14ad2f0ba0 unit testing: catch -> doctest (faster compile, thread-safe) 2021-11-08 11:55:21 -08:00
Michael Scire
fcc7ce49d9 sm/tipc: ensure technical-correctness of sins (no page cost) 2021-11-06 20:05:32 -07:00
Michael Scire
f98c7cba98 sm: save 0x5000 of memory by sinning 2021-11-06 19:33:08 -07:00
Léo Lam
496adb0018 Minor header fixes to reduce parsing issues with Clang (#1700)
* Work around Clang's incomplete C++20 support for omitting typename

* vapours: fix Clang error about missing return in constexpr function

* stratosphere: fix call to non-constexpr strlen in constexpr function

strlen being constexpr is a non-compliant GCC extension; Clang
explicitly rejects it: https://reviews.llvm.org/D23692

* stratosphere: add a bunch of missing override specifiers

* stratosphere: work around Clang consteval bug

Minimal example: https://godbolt.org/z/MoM64v93M

The issue seems to be that Clang does not consider f(x) to be a
constant expression if x comes from a template argument that isn't
a non-type auto template argument (???)

We can work around this by relaxing GetMessageHeaderForCheck (by using
constexpr instead of consteval). This produces no functional changes
because the result of GetMessageHeaderForCheck() is assigned to a
constexpr variable, so the result is guaranteed to be computed
at compile-time.

* stratosphere: fix missing require clauses in definitions

GCC not requiring the require clauses to be repeated for member
definitions is actually a compiler bug:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96830

Clang rejects declarations with missing require clauses.

* Fix ALWAYS_INLINE_LAMBDA and parameter list relative order

While GCC doesn't seem to care about the position of the always_inline
attribute relative to the parameter list, Clang is very picky
and requires the attribute to appear after the parameter list
(and before a trailing return type)

* stratosphere: fix static constexpr member variable with incomplete type

GCC accepts this for some reason (because of the lambda?) but Clang
correctly rejects this.
2021-11-06 18:19:34 -07:00
Michael Scire
09074798cd KScheduler big brain strat for mdscr_el1 cfg change 2021-11-05 23:38:43 -07:00
Michael Scire
e256261b80 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "ff2883361"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "ff2883361"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-11-04 13:40:55 -07:00
Michael Scire
47218f0da8 ams: overhaul fs.mitm romfs ownership, bump to 1.2.4 2021-11-04 13:38:41 -07:00
Michael Scire
409a48ec73 docs: add changelog for 1.2.3 2021-11-04 00:23:23 -07:00
Michael Scire
0c93cefd39 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "e7b84767c"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "e7b84767c"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-11-04 00:00:26 -07:00
Michael Scire
0bbc907907 git subrepo pull (merge) libraries
subrepo:
  subdir:   "libraries"
  merged:   "8764bd406"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "12d0ba172"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-11-03 23:59:17 -07:00
Michael Scire
4a4a1f0e87 ams: bump api version to 1.2.3 2021-11-03 23:57:30 -07:00
Michael Scire
705dd95375 dmnt2: fix gdb register output 2021-11-03 23:56:25 -07:00
Michael Scire
2d5751356b creport: fix errant ) 2021-11-03 23:56:25 -07:00
Michael Scire
b140834b7e dmnt2: try to avoid writing out of bounds when generating packets 2021-11-03 23:56:25 -07:00
Michael Scire
e3d2af6b3f dmnt2: fix module name detection, add auto-break on hb nro launch 2021-11-03 23:56:25 -07:00
jam1garner
c17ad1e0e3 dmnt2: fix missing null-terminator for invalid command error 2021-11-03 23:56:25 -07:00
Michael Scire
6145b3b72c dmnt2: detect thread name, add monitor get mapping(s), increase buffer sizes 2021-11-03 23:56:25 -07:00
Michael Scire
aba7e4ca7d dmnt2: remove memory-map output which does nothing for us 2021-11-03 23:56:25 -07:00
Michael Scire
4cc5e9cdfd kern/dmnt2: allow retrieval of process info via extension
This also fixes ctrl-c break in gdbstub, and fixes crash on unknown monitor cmd.
2021-11-03 23:56:25 -07:00
Michael Scire
ca0308c7ca dmnt2: first pass at wait-for-application 2021-11-03 23:56:25 -07:00
Michael Scire
1d908295fe dmnt2: add monitor get base, TODO responses for monitor wait * 2021-11-03 23:56:25 -07:00
Michael Scire
277bd101c5 dmnt2: add memory-map read, improve module shared-lib names 2021-11-03 23:56:25 -07:00
Michael Scire
3dce23773a Adding setting usage to dmnt2 means dmnt2 needs settings access 2021-11-03 23:56:25 -07:00
Michael Scire
4489513f7c dmnt: enable experimental standalone usage of gdbstub, while starlink is in dev 2021-11-03 23:56:25 -07:00
Michael Scire
5eabca7f04 ams.mitm: more romfs building space/time tradeoffs.
This is needed for Animal Crossing 2.0.0, which has >99000 fucking files.

We now do several passes over dir/file tables instead of one pass,
doing entire hash tables before we touch dir/file tables. Thus we
no longer need to simultaneously allocate hash table and dir/file table space.

In addition, we now do repeated passes building a segment of hash tables
at a time, when insufficient memory is available. Similar is also now the
case for file/dir tables, we try 0x40000 work buffer and divide by 2
until we successfully alloc. We don't allow a work buffer <0x4000, for
write/perf reasons. If a game triggers that, let me know I guess.

Hard to imagine a worse torture-test for this code than animal crossing.
2021-11-03 23:52:38 -07:00
Michael Scire
a73e0e8f16 ams: add arm/armv8a to gitignore 2021-10-31 11:20:32 -07:00
Michael Scire
2161365f4f ams: update for awareness of architecture revision 2021-10-31 11:18:17 -07:00
Michael Scire
258a83684e creport: print symbols if they're present 2021-10-30 14:18:00 -07:00
Michael Scire
d2a757c39e git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "ceff2f371"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "ceff2f371"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-10-30 11:21:54 -07:00
Michael Scire
6cf5205a28 spl: fix legacy physical keyslot compatibility 2021-10-30 11:21:25 -07:00
135 changed files with 9479 additions and 19232 deletions

4
.gitignore vendored
View File

@@ -91,7 +91,11 @@ dkms.conf
**/out
**/build
**/build_nintendo_nx_arm64
**/build_nintendo_nx_arm64_armv8a
**/build_nintendo_nx_arm
**/build_nintendo_nx_arm_armv8a
**/build_nintendo_nx_arm_armv7a
**/build_nintendo_nx_arm_armv4t
**/build_nintendo_nx_x64
**/build_nintendo_nx_x86

View File

@@ -112,6 +112,7 @@ dist-no-debug: all
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000420
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000B240
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D609
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D623
cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008/exefs.nsp
cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000000D/exefs.nsp
@@ -125,6 +126,7 @@ dist-no-debug: all
cp stratosphere/pgl/pgl.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042/exefs.nsp
cp stratosphere/LogManager/LogManager.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000420/exefs.nsp
cp stratosphere/htc/htc.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000B240/exefs.nsp
cp stratosphere/dmnt.gen2/dmnt.gen2.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D609/exefs.nsp
cp stratosphere/TioServer/TioServer.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D623/exefs.nsp
@build_romfs atmosphere-$(AMSVER)/stratosphere_romfs atmosphere-$(AMSVER)/atmosphere/stratosphere.romfs
rm -r atmosphere-$(AMSVER)/stratosphere_romfs

View File

@@ -1,4 +1,57 @@
# Changelog
## 1.2.6
+ Support was added for 13.2.1.
+ A number of minor issues were fixed and improvements were made, including:
+ A minor performance improvement was implemented in service table dispatch by sorting and binary-searching the service command table instead of using linear search.
+ Static initialization logic in Atmosphere was made much more regular.
+ General system stability improvements to enhance the user's experience.
## 1.2.5
+ Support was added for 13.2.0.
+ A number of minor issues were fixed and improvements were made, including:
+ A bug was fixed that caused `mesosphère` to underreport the total memory size by 8MB for certain games which use newer system-resource-size memory management.
+ This caused FIFA 19 to crash, and possibly other issues.
+ Memory management changes were made to `sm` that save 0x5000 of memory.
+ A microoptimization was made to the way `mesosphère` manages updating the debug register for hardware single-step support.
+ Support was fixed for enabling `usb!usb30_force_enabled` on 13.0.0+.
+ The work-in-progress unit testing framework was updated to use doctest instead of catch2.
+ General system stability improvements to enhance the user's experience.
## 1.2.4
+ Changes were made to the way fs.mitm builds images when providing a layeredfs romfs.
+ Cache management (to avoid unnecessary rebuild) was revised, to add a grace period of ~500ms-1s between process closing romfs image and ams.mitm needing to rebuild if romfs is re-opened.
+ This makes our cache much more effective, previously we were re-building romfs several times.
+ RomFS image ownership was overhauled, with a new reference-counting implementation added (used to implement the above grace period).
+ Certain games (e.g. Puyo Puyo Tetris 2, probably others) were sensitive to this timing, and could use access patterns which would trigger creation of romfs image while previous romfs image was in the middle of destructor.
+ This could cause a fatal error, because the destructor for the old image could run simultaneously with building the new image.
+ This also provides a speedup versus the 1.2.3 code, with Animal Crossing now taking ~8 fewer seconds to get past the Nintendo Switch logo.
+ General system stability improvements to enhance the user's experience.
## 1.2.3
+ Because ams.TMA is taking longer to develop than expected, experimental support for Atmosphère's gdbstub as a standalone is now available.
+ To enable it, set `atmosphere!enable_standalone_gdbstub` = u8!0x1 in system_settings.ini.
+ The standalone also requires `atmosphere!enable_htc` = u8!0x0, but this should be the case for everyone since ams.TMA isn't actually usable yet.
+ Once enabled, open the devkitPro provided-gdb (`aarch64-none-elf-gdb` for 64-bit or `arm-none-eabi-gdb` for 32-bit).
+ The standalone stub exposes itself on port 22225 -- so the command to connect is `target extended-remote <ip address>:22225`.
+ Type `info os processes` to get a list of process IDs that can be attached to.
+ The stub should work on both system programs, games, and homebrew -- but please note that debugging certain processes (like sockets) can cause hang due to the stub using them itself.
+ Software break-points, hardware break-points, hardware watch-points, and hardware single-step are all supported/implemented.
+ The following monitor commands are currently supported:
+ `monitor get info`: Get process info, address space layout, and information on modules.
+ `monitor get mappings`: Get all memory mappings.
+ `monitor get mapping <addr>`: Get the memory mapping for a specific address.
+ `monitor wait application`: Causes the stub to wait for an application to be launched. The next application will be started suspended.
+ User is expected to send `attach <pid>` after launching, which will cause attach-on-first-instruction. Failure to attach may cause system instability, this probably needs work.
+ **Please Note**: The GDBstub is new and may have bugs/need work. If you find issues, please report them to SciresM#0524 -- all help finding/fixing bugs is appreciated, here.
+ Generally speaking, if you would like to report information about fixes needed/discuss development of the gdbstub, join ReSwitched's #dev-support channel.
+ Changes were made to the way fs.mitm builds images when providing a layeredfs romfs.
+ Animal Crossing's 2.0.0 update contains >99000 files, and has tables so big that we ran out of memory even after the optimizations made in 0.10.5.
+ Previously, we used fixed-sized 0x40000 work buffers for file/directory tables and simultaneously built hash/content tables in one loop over files/directories.
+ We now iterate over the file/directory tables multiple times, first once to determine the hash table indices, then repeatedly to build hash tables, then once to build content tables.
+ We also now allow smaller-than-0x40000 work buffers, trying half-as-big buffers until allocation succeeds (or work buffer would be <0x4000, which is a safeguard against truly horrible performance).
+ There is a slight speed penalty to these changes, but it's on the order of seconds for the worst case (Animal Crossing) and trivial for most games with reasonable tables.
+ If you encounter a game that exhausts ams.mitm's memory (crashing it) when loading layeredfs mods, please contact `SciresM#0524`.
+ It's really hard to imagine any game being worse than Animal Crossing, but if it happens again I will drop everything to fix it as usual.
+ `creport` now attempts to parse symbol tables if present.
+ If a game executable has a symbol for a given address, the function-relative-offset will now be printed after the module-relative-offset.
+ General system stability improvements to enhance the user's experience.
## 1.2.2
+ A number of fixes were made to Atmosphère's implementation of the new "sprofile" service added in 13.0.0.
+ Nintendo is finally transmitting data over the internet to certain consoles, which has allowed for validating our service implementation.

View File

@@ -249,6 +249,8 @@ namespace ams::nxboot {
return ams::TargetFirmware_12_1_0;
} else if (std::memcmp(package1 + 0x10, "20210805", 8) == 0) {
return ams::TargetFirmware_13_0_0;
} else if (std::memcmp(package1 + 0x10, "20220105", 8) == 0) {
return ams::TargetFirmware_13_2_1;
}
break;
default:

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master
commit = 4d0f1b79246d7d2c19219bc7da153fdfef165f57
parent = 94e18b8c93e6013aaa647d31d54cb3f06e391a46
commit = 0d161b8588aa6482b84f3c44dd001055b01a047f
parent = 4efa5d7dd0bfbdf89a6261af0aef3878ca784b05
method = merge
cmdver = 0.4.1

View File

@@ -0,0 +1,11 @@
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
include $(DEVKITPRO)/devkitARM/base_rules
export ATMOSPHERE_DEFINES += -DATMOSPHERE_ARCH_ARM_V4T
export ATMOSPHERE_SETTINGS +=
export ATMOSPHERE_CFLAGS +=
export ATMOSPHERE_CXXFLAGS +=
export ATMOSPHERE_ASFLAGS +=

View File

@@ -0,0 +1,11 @@
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
include $(DEVKITPRO)/devkitA64/base_rules
export ATMOSPHERE_DEFINES += -DATMOSPHERE_ARCH_ARM_V8A
export ATMOSPHERE_SETTINGS +=
export ATMOSPHERE_CFLAGS +=
export ATMOSPHERE_CXXFLAGS +=
export ATMOSPHERE_ASFLAGS +=

View File

@@ -39,6 +39,9 @@ export ATMOSPHERE_ARCH_NAME := arm64
export ATMOSPHERE_BOARD_NAME := nintendo_nx
export ATMOSPHERE_OS_NAME := horizon
export ATMOSPHERE_SUB_ARCH_DIR = armv8a
export ATMOSPHERE_SUB_ARCH_NAME = armv8a
export ATMOSPHERE_CPU_EXTENSIONS := arm_crypto_extension aarch64_crypto_extension
else ifeq ($(ATMOSPHERE_CPU),arm7tdmi)
export ATMOSPHERE_ARCH_DIR := arm
@@ -49,6 +52,9 @@ export ATMOSPHERE_ARCH_NAME := arm
export ATMOSPHERE_BOARD_NAME := nintendo_nx
export ATMOSPHERE_OS_NAME := horizon
export ATMOSPHERE_SUB_ARCH_DIR = armv4t
export ATMOSPHERE_SUB_ARCH_NAME = armv4t
export ATMOSPHERE_CPU_EXTENSIONS :=
endif
@@ -64,6 +70,9 @@ export ATMOSPHERE_ARCH_NAME := arm64
export ATMOSPHERE_BOARD_NAME := qemu_virt
export ATMOSPHERE_OS_NAME := horizon
export ATMOSPHERE_SUB_ARCH_DIR = armv8a
export ATMOSPHERE_SUB_ARCH_NAME = armv8a
export ATMOSPHERE_CPU_EXTENSIONS := arm_crypto_extension aarch64_crypto_extension
endif
@@ -85,14 +94,27 @@ export ATMOSPHERE_BOARD_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/board/$(ATMOSP
export ATMOSPHERE_OS_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/os/$(ATMOSPHERE_OS_DIR)
export ATMOSPHERE_CPU_MAKE_DIR := $(ATMOSPHERE_ARCH_MAKE_DIR)/cpu/$(ATMOSPHERE_CPU_DIR)
ifneq ($(strip $(ATMOSPHERE_SUB_ARCH_NAME)),)
export ATMOSPHERE_LIBRARY_DIR := lib_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)_$(ATMOSPHERE_SUB_ARCH_NAME)
export ATMOSPHERE_BUILD_DIR := build_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)_$(ATMOSPHERE_SUB_ARCH_NAME)
else
export ATMOSPHERE_LIBRARY_DIR := lib_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)
export ATMOSPHERE_BUILD_DIR := build_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)
endif
include $(ATMOSPHERE_ARCH_MAKE_DIR)/arch.mk
include $(ATMOSPHERE_BOARD_MAKE_DIR)/board.mk
include $(ATMOSPHERE_OS_MAKE_DIR)/os.mk
include $(ATMOSPHERE_CPU_MAKE_DIR)/cpu.mk
ifneq ($(strip $(ATMOSPHERE_SUB_ARCH_NAME)),)
export ATMOSPHERE_SUB_ARCH_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/arch/$(ATMOSPHERE_SUB_ARCH_DIR)
include $(ATMOSPHERE_SUB_ARCH_MAKE_DIR)/arch.mk
endif
#---------------------------------------------------------------------------------
# get atmosphere git revision information
#---------------------------------------------------------------------------------
@@ -125,12 +147,20 @@ DATA := data
INCLUDES := include
GENERAL_SOURCE_DIRS=$1 $(foreach d,$(filter-out $1/arch $1/board $1/os $1/cpu $1,$(wildcard $1/*)),$(if $(wildcard $d/.),$(filter-out $d,$(call GENERAL_SOURCE_DIRS,$d)) $d,))
SPECIFIC_SOURCE_DIRS=$(if $(wildcard $1/$2/$3/.*),$1/$2/$3 $(call DIR_WILDCARD,$1/$2/$3),$(if $(wildcard $1/$2/generic/.*), $1/$2/generic $(call DIR_WILDCARD,$1/$2/generic),))
ifneq ($(strip $(ATMOSPHERE_SUB_ARCH_NAME)),)
SPECIFIC_SOURCE_DIRS_ARCH=$(if $(wildcard $1/$2/$3/.*),$1/$2/$3 $(call DIR_WILDCARD,$1/$2/$3),$(if $(wildcard $1/$2/$4/.*),$1/$2/$4 $(call DIR_WILDCARD,$1/$2/$4),$(if $(wildcard $1/$2/generic/.*), $1/$2/generic $(call DIR_WILDCARD,$1/$2/generic),)))
else
SPECIFIC_SOURCE_DIRS_ARCH=$(if $(wildcard $1/$2/$3/.*),$1/$2/$3 $(call DIR_WILDCARD,$1/$2/$3),$(if $(wildcard $1/$2/generic/.*), $1/$2/generic $(call DIR_WILDCARD,$1/$2/generic),))
endif
UNFILTERED_SOURCE_DIRS=$1 $(foreach d,$(wildcard $1/*),$(if $(wildcard $d/.),$(call DIR_WILDCARD,$d) $d,))
ALL_SOURCE_DIRS=$(foreach d,$(call GENERAL_SOURCE_DIRS,$1), \
$d \
$(call SPECIFIC_SOURCE_DIRS,$d,arch,$(ATMOSPHERE_ARCH_DIR)) \
$(call SPECIFIC_SOURCE_DIRS_ARCH,$d,arch,$(ATMOSPHERE_ARCH_DIR),$(ATMOSPHERE_SUB_ARCH_DIR)) \
$(call SPECIFIC_SOURCE_DIRS,$d,board,$(ATMOSPHERE_BOARD_DIR)) \
$(call SPECIFIC_SOURCE_DIRS,$d,os,$(ATMOSPHERE_OS_DIR)) \
$(call SPECIFIC_SOURCE_DIRS,$d,cpu,$(ATMOSPHERE_ARCH_DIR)/$(ATMOSPHERE_CPU_DIR)) \
@@ -147,7 +177,7 @@ FIND_SOURCE_FILES=$(foreach dir,$1,$(filter-out $(notdir $(wildcard $(dir)/*.arc
$(notdir $(wildcard $(dir)/*.os.*.$2)) \
$(notdir $(wildcard $(dir)/.cpu.*.$2)), \
$(notdir $(wildcard $(dir)/*.$2)))) \
$(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES,$(dir),arch,$(ATMOSPHERE_ARCH_NAME),$2)) \
$(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES_EX,$(dir),arch,$(ATMOSPHERE_ARCH_NAME) $(ATMOSPHERE_SUB_ARCH_NAME),$2)) \
$(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES,$(dir),board,$(ATMOSPHERE_BOARD_NAME),$2)) \
$(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES,$(dir),os,$(ATMOSPHERE_OS_NAME),$2)) \
$(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES_EX,$(dir),cpu,$(ATMOSPHERE_CPU_NAME) $(ATMOSPHERE_CPU_EXTENSIONS),$2))

View File

@@ -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 0x13 in Nintendo's code. */
constexpr inline int CurrentBootloaderVersion = 0xF;
constexpr inline int MinimumValidDataVersion = 0; /* We allow older package2 to load; this value is currently 0x14 in Nintendo's code. */
constexpr inline int CurrentBootloaderVersion = 0x10;
struct Package2Meta {
using Magic = util::FourCC<'P','K','2','1'>;

View File

@@ -167,6 +167,7 @@ namespace ams::fuse {
}
constexpr const TargetFirmware FuseVersionIncrementFirmwares[] = {
TargetFirmware_13_2_1,
TargetFirmware_12_0_2,
TargetFirmware_11_0_0,
TargetFirmware_10_0_0,

View File

@@ -133,7 +133,7 @@ namespace ams::kern::arch::arm {
PriorityLevel_Scheduler = 2,
};
private:
static inline u32 s_mask[cpu::NumCores];
static constinit inline u32 s_mask[cpu::NumCores];
private:
volatile GicDistributor *m_gicd;
volatile GicCpuInterface *m_gicc;

View File

@@ -40,6 +40,12 @@
/* of the right side, and so this does not actually cost any space. */
#define MESOSPHERE_ENABLE_DEVIRTUALIZED_DYNAMIC_CAST
/* NOTE: This enables usage of KDebug handles as parameter for svc::GetInfo */
/* calls which require a process parameter. This enables a debugger to obtain */
/* address space/layout information, for example. However, it changes abi, and so */
/* this define allows toggling the extension. */
#define MESOSPHERE_ENABLE_GET_INFO_OF_DEBUG_PROCESS
/* NOTE: This uses currently-reserved bits inside the MapRange capability */
/* in order to support large physical addresses (40-bit instead of 36). */
/* This is toggleable in order to disable it if N ever uses those bits. */

View File

@@ -62,8 +62,7 @@ namespace ams::kern {
KThread *m_idle_thread;
util::Atomic<KThread *> m_current_thread;
public:
constexpr KScheduler() : m_state(), m_is_active(false), m_core_id(0), m_last_context_switch_time(0), m_idle_thread(nullptr), m_current_thread(nullptr)
{
constexpr KScheduler() : m_state(), m_is_active(false), m_core_id(0), m_last_context_switch_time(0), m_idle_thread(nullptr), m_current_thread(nullptr) {
m_state.needs_scheduling = true;
m_state.interrupt_task_runnable = false;
m_state.should_count_idle = false;

View File

@@ -44,7 +44,7 @@ namespace ams::kern::arch::arm64::cpu {
class KPerformanceCounterInterruptHandler : public KInterruptHandler {
private:
static inline KLightLock s_lock;
static constinit inline KLightLock s_lock;
private:
u64 m_counter;
s32 m_which;

View File

@@ -59,7 +59,7 @@ namespace ams::kern::svc {
/* Set omit-frame-pointer to prevent GCC from emitting MOV X29, SP instructions. */
#pragma GCC push_options
#pragma GCC optimize ("-O2")
#pragma GCC optimize ("-O3")
#pragma GCC optimize ("omit-frame-pointer")
AMS_SVC_FOREACH_KERN_DEFINITION(DECLARE_SVC_STRUCT, _)

View File

@@ -114,6 +114,7 @@ namespace ams::kern::board::nintendo::nx {
ams::svc::DeviceName_Hda,
ams::svc::DeviceName_Isp2,
ams::svc::DeviceName_Sata,
ams::svc::DeviceName_Vi,
ams::svc::DeviceName_XusbHost,
ams::svc::DeviceName_XusbDev,
ams::svc::DeviceName_Tsec,
@@ -548,11 +549,11 @@ namespace ams::kern::board::nintendo::nx {
/* Print the interrupt. */
{
constexpr auto GetBits = [] ALWAYS_INLINE_LAMBDA (u32 value, size_t ofs, size_t count) {
constexpr auto GetBits = [](u32 value, size_t ofs, size_t count) ALWAYS_INLINE_LAMBDA {
return (value >> ofs) & ((1u << count) - 1);
};
constexpr auto GetBit = [GetBits] ALWAYS_INLINE_LAMBDA (u32 value, size_t ofs) {
constexpr auto GetBit = [GetBits](u32 value, size_t ofs) ALWAYS_INLINE_LAMBDA {
return (value >> ofs) & 1u;
};

View File

@@ -312,7 +312,7 @@ namespace ams::kern::board::nintendo::nx {
size_t KSystemControl::Init::GetApplicationPoolSize() {
/* Get the base pool size. */
const size_t base_pool_size = [] ALWAYS_INLINE_LAMBDA () -> size_t {
const size_t base_pool_size = []() ALWAYS_INLINE_LAMBDA -> size_t {
switch (GetMemoryArrangeForInit()) {
case smc::MemoryArrangement_4GB:
default:
@@ -336,7 +336,7 @@ namespace ams::kern::board::nintendo::nx {
size_t KSystemControl::Init::GetAppletPoolSize() {
/* Get the base pool size. */
const size_t base_pool_size = [] ALWAYS_INLINE_LAMBDA () -> size_t {
const size_t base_pool_size = []() ALWAYS_INLINE_LAMBDA -> size_t {
switch (GetMemoryArrangeForInit()) {
case smc::MemoryArrangement_4GB:
default:
@@ -490,7 +490,7 @@ namespace ams::kern::board::nintendo::nx {
if (AMS_LIKELY(s_initialized_random_generator)) {
return KSystemControlBase::GenerateUniformRange(min, max, [] ALWAYS_INLINE_LAMBDA () -> u64 { return s_random_generator.GenerateRandomU64(); });
return KSystemControlBase::GenerateUniformRange(min, max, []() ALWAYS_INLINE_LAMBDA -> u64 { return s_random_generator.GenerateRandomU64(); });
} else {
return KSystemControlBase::GenerateUniformRange(min, max, GenerateRandomU64FromSmc);
}
@@ -680,4 +680,4 @@ namespace ams::kern::board::nintendo::nx {
Kernel::GetMemoryManager().Close(KPageTable::GetHeapPhysicalAddress(address), size / PageSize);
}
}
}

View File

@@ -19,7 +19,7 @@ namespace ams::kern {
namespace {
constexpr std::tuple<KMemoryState, const char *> MemoryStateNames[] = {
constexpr const std::pair<KMemoryState, const char *> MemoryStateNames[] = {
{KMemoryState_Free , "----- Free -----"},
{KMemoryState_Io , "Io "},
{KMemoryState_Static , "Static "},
@@ -41,6 +41,7 @@ namespace ams::kern {
{KMemoryState_Kernel , "Kernel "},
{KMemoryState_GeneratedCode , "GeneratedCode "},
{KMemoryState_CodeOut , "CodeOut "},
{KMemoryState_Coverage , "Coverage "},
};
constexpr const char *GetMemoryStateName(KMemoryState state) {

View File

@@ -195,7 +195,7 @@ namespace ams::kern {
static_assert(KMemoryManager::Pool_Secure == KMemoryManager::Pool_System);
/* Get Secure pool size. */
const size_t secure_pool_size = [] ALWAYS_INLINE_LAMBDA (auto target_firmware) -> size_t {
const size_t secure_pool_size = [](auto target_firmware) ALWAYS_INLINE_LAMBDA -> size_t {
constexpr size_t LegacySecureKernelSize = 8_MB; /* KPageBuffer pages, other small kernel allocations. */
constexpr size_t LegacySecureMiscSize = 1_MB; /* Miscellaneous pages for secure process mapping. */
constexpr size_t LegacySecureHeapSize = 24_MB; /* Heap pages for secure process mapping (fs). */
@@ -274,4 +274,4 @@ namespace ams::kern {
}
}
}

View File

@@ -2398,7 +2398,7 @@ namespace ams::kern {
size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
size_t tot_size = cur_size;
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
/* Ensure the address is linear mapped. */
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
@@ -2484,7 +2484,7 @@ namespace ams::kern {
size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
size_t tot_size = cur_size;
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
/* Ensure the address is linear mapped. */
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
@@ -2943,7 +2943,7 @@ namespace ams::kern {
size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
size_t tot_size = cur_size;
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
/* Ensure the address is linear mapped. */
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
@@ -3023,7 +3023,7 @@ namespace ams::kern {
size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
size_t tot_size = cur_size;
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
/* Ensure the address is linear mapped. */
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
@@ -3092,7 +3092,7 @@ namespace ams::kern {
size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
size_t tot_size = cur_size;
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
/* Ensure the address is linear mapped. */
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
@@ -3172,7 +3172,7 @@ namespace ams::kern {
size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
size_t tot_size = cur_size;
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
/* Ensure the address is linear mapped. */
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());

View File

@@ -893,7 +893,7 @@ namespace ams::kern {
size_t KProcess::GetTotalUserPhysicalMemorySize() const {
/* Get the amount of free and used size. */
const size_t free_size = m_resource_limit->GetFreeValue(ams::svc::LimitableResource_PhysicalMemoryMax);
const size_t used_size = this->GetUsedNonSystemUserPhysicalMemorySize();
const size_t used_size = this->GetUsedUserPhysicalMemorySize();
const size_t max_size = m_max_process_memory;
if (used_size + free_size > max_size) {

View File

@@ -15,10 +15,10 @@
*/
#include <mesosphere.hpp>
namespace ams::kern {
#pragma GCC push_options
#pragma GCC optimize ("-O3")
#pragma GCC push_options
#pragma GCC optimize ("-O3")
namespace ams::kern {
bool KScheduler::s_scheduler_update_needed;
KScheduler::LockType KScheduler::s_scheduler_lock;
@@ -249,22 +249,18 @@ namespace ams::kern {
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
/* Ensure the single-step bit in mdscr reflects the correct single-step state for the new thread. */
/* NOTE: Per ARM docs, changing the single-step bit requires a "context synchronization event" to */
/* be sure that our new configuration takes. However, there are three types of synchronization event: */
/* Taking an exception, returning from an exception, and ISB. The single-step bit change only matters */
/* in EL0...which implies a return-from-exception has occurred since we set the bit. Thus, forcing */
/* an ISB is unnecessary, and we can modify the register safely and be confident it will affect the next */
/* userland instruction executed. */
cpu::MonitorDebugSystemControlRegisterAccessor().SetSoftwareStep(next_thread->IsSingleStep()).Store();
#endif
/* Switch the current process, if we're switching processes. */
if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) {
KProcess::Switch(cur_process, next_process);
} else {
/* The single-step bit set up above requires an instruction synchronization barrier, to ensure */
/* the state change takes before we actually perform a return which might break-to-step. */
/* KProcess::Switch performs an isb incidentally, and so when we're changing process we */
/* can piggy-back off of that isb to avoid unnecessarily emptying the pipeline twice. */
/* However, this means that when we're switching to thread in a different process, */
/* we must ensure that we still isb. In practice, gcc will deduplicate into a single isb. */
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
cpu::InstructionMemoryBarrier();
#endif
}
/* Set the new thread. */
@@ -602,6 +598,6 @@ namespace ams::kern {
}
}
#pragma GCC pop_options
}
#pragma GCC pop_options

View File

@@ -15,10 +15,10 @@
*/
#include <mesosphere.hpp>
namespace ams::kern {
#pragma GCC push_options
#pragma GCC optimize ("-O3")
#pragma GCC push_options
#pragma GCC optimize ("-O3")
namespace ams::kern {
namespace ipc {
@@ -1385,8 +1385,6 @@ namespace ams::kern {
this->NotifyAvailable(svc::ResultSessionClosed());
}
#pragma GCC pop_options
void KServerSession::Dump() {
MESOSPHERE_ASSERT_THIS();
@@ -1420,3 +1418,5 @@ namespace ams::kern {
}
}
#pragma GCC pop_options

View File

@@ -96,7 +96,7 @@ namespace ams::kern {
s_initialized_random_generator = true;
}
return KSystemControlBase::GenerateUniformRange(min, max, [] ALWAYS_INLINE_LAMBDA () -> u64 { return s_random_generator.GenerateRandomU64(); });
return KSystemControlBase::GenerateUniformRange(min, max, []() ALWAYS_INLINE_LAMBDA -> u64 { return s_random_generator.GenerateRandomU64(); });
}
/* System Initialization. */
@@ -194,7 +194,7 @@ namespace ams::kern {
KScopedInterruptDisable intr_disable;
KScopedSpinLock lk(s_random_lock);
return KSystemControlBase::GenerateUniformRange(min, max, [] ALWAYS_INLINE_LAMBDA () -> u64 { return s_random_generator.GenerateRandomU64(); });
return KSystemControlBase::GenerateUniformRange(min, max, []() ALWAYS_INLINE_LAMBDA -> u64 { return s_random_generator.GenerateRandomU64(); });
}
u64 KSystemControlBase::GenerateRandomU64() {
@@ -292,4 +292,4 @@ namespace ams::kern {
Kernel::GetMemoryManager().Close(KPageTable::GetHeapPhysicalAddress(address), size / PageSize);
}
}
}

View File

@@ -1183,7 +1183,7 @@ namespace ams::kern {
KScopedSchedulerLock sl;
/* Determine if this is the first termination request. */
const bool first_request = [&] ALWAYS_INLINE_LAMBDA () -> bool {
const bool first_request = [&]() ALWAYS_INLINE_LAMBDA -> bool {
/* Perform an atomic compare-and-swap from false to true. */
bool expected = false;
return m_termination_requested.CompareExchangeStrong(expected, true);

View File

@@ -48,7 +48,7 @@ namespace ams::kern::svc {
/* Check whether the address is aligned. */
const bool aligned = util::IsAligned(phys_addr, PageSize);
auto QueryIoMappingFromPageTable = [&] ALWAYS_INLINE_LAMBDA (uint64_t phys_addr, size_t size) -> Result {
auto QueryIoMappingFromPageTable = [&](uint64_t phys_addr, size_t size) ALWAYS_INLINE_LAMBDA -> Result {
/* The size must be non-zero. */
R_UNLESS(size > 0, svc::ResultInvalidSize());

View File

@@ -277,6 +277,9 @@ namespace ams::kern::svc {
}
Result WriteDebugProcessMemory(ams::svc::Handle debug_handle, uintptr_t buffer, uintptr_t address, size_t size) {
/* Only allow invoking the svc on development hardware. */
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
/* Validate address / size. */
R_UNLESS(size > 0, svc::ResultInvalidSize());
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());

View File

@@ -38,6 +38,80 @@ namespace ams::kern::svc {
return ResultSuccess();
}
Result GetInfoImpl(u64 *out, ams::svc::InfoType info_type, KProcess *process) {
switch (info_type) {
case ams::svc::InfoType_CoreMask:
*out = process->GetCoreMask();
break;
case ams::svc::InfoType_PriorityMask:
*out = process->GetPriorityMask();
break;
case ams::svc::InfoType_AliasRegionAddress:
*out = GetInteger(process->GetPageTable().GetAliasRegionStart());
break;
case ams::svc::InfoType_AliasRegionSize:
*out = process->GetPageTable().GetAliasRegionSize();
break;
case ams::svc::InfoType_HeapRegionAddress:
*out = GetInteger(process->GetPageTable().GetHeapRegionStart());
break;
case ams::svc::InfoType_HeapRegionSize:
*out = process->GetPageTable().GetHeapRegionSize();
break;
case ams::svc::InfoType_TotalMemorySize:
*out = process->GetTotalUserPhysicalMemorySize();
break;
case ams::svc::InfoType_UsedMemorySize:
*out = process->GetUsedUserPhysicalMemorySize();
break;
case ams::svc::InfoType_AslrRegionAddress:
*out = GetInteger(process->GetPageTable().GetAliasCodeRegionStart());
break;
case ams::svc::InfoType_AslrRegionSize:
*out = process->GetPageTable().GetAliasCodeRegionSize();
break;
case ams::svc::InfoType_StackRegionAddress:
*out = GetInteger(process->GetPageTable().GetStackRegionStart());
break;
case ams::svc::InfoType_StackRegionSize:
*out = process->GetPageTable().GetStackRegionSize();
break;
case ams::svc::InfoType_SystemResourceSizeTotal:
*out = process->GetTotalSystemResourceSize();
break;
case ams::svc::InfoType_SystemResourceSizeUsed:
*out = process->GetUsedSystemResourceSize();
break;
case ams::svc::InfoType_ProgramId:
*out = process->GetProgramId();
break;
case ams::svc::InfoType_UserExceptionContextAddress:
*out = GetInteger(process->GetProcessLocalRegionAddress());
break;
case ams::svc::InfoType_TotalNonSystemMemorySize:
*out = process->GetTotalNonSystemUserPhysicalMemorySize();
break;
case ams::svc::InfoType_UsedNonSystemMemorySize:
*out = process->GetUsedNonSystemUserPhysicalMemorySize();
break;
case ams::svc::InfoType_IsApplication:
*out = process->IsApplication();
break;
case ams::svc::InfoType_FreeThreadCount:
if (KResourceLimit *resource_limit = process->GetResourceLimit(); resource_limit != nullptr) {
const auto current_value = resource_limit->GetCurrentValue(ams::svc::LimitableResource_ThreadCountMax);
const auto limit_value = resource_limit->GetLimitValue(ams::svc::LimitableResource_ThreadCountMax);
*out = limit_value - current_value;
} else {
*out = 0;
}
break;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
return ResultSuccess();
}
Result GetInfo(u64 *out, ams::svc::InfoType info_type, ams::svc::Handle handle, u64 info_subtype) {
switch (info_type) {
case ams::svc::InfoType_CoreMask:
@@ -66,77 +140,34 @@ namespace ams::kern::svc {
/* Get the process from its handle. */
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(handle);
#if defined(MESOSPHERE_ENABLE_GET_INFO_OF_DEBUG_PROCESS)
/* If we the process is valid, use it. */
if (process.IsNotNull()) {
return GetInfoImpl(out, info_type, process.GetPointerUnsafe());
}
/* Otherwise, as a mesosphere extension check if we were passed a usable KDebug. */
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(handle);
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
/* Get the process from the debug object. */
/* TODO: ResultInvalidHandle()? */
R_UNLESS(debug->IsAttached(), svc::ResultProcessTerminated());
R_UNLESS(debug->OpenProcess(), svc::ResultProcessTerminated());
/* Close the process when we're done. */
ON_SCOPE_EXIT { debug->CloseProcess(); };
/* Return the info. */
return GetInfoImpl(out, info_type, debug->GetProcessUnsafe());
#else
/* Verify that the process is valid. */
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
switch (info_type) {
case ams::svc::InfoType_CoreMask:
*out = process->GetCoreMask();
break;
case ams::svc::InfoType_PriorityMask:
*out = process->GetPriorityMask();
break;
case ams::svc::InfoType_AliasRegionAddress:
*out = GetInteger(process->GetPageTable().GetAliasRegionStart());
break;
case ams::svc::InfoType_AliasRegionSize:
*out = process->GetPageTable().GetAliasRegionSize();
break;
case ams::svc::InfoType_HeapRegionAddress:
*out = GetInteger(process->GetPageTable().GetHeapRegionStart());
break;
case ams::svc::InfoType_HeapRegionSize:
*out = process->GetPageTable().GetHeapRegionSize();
break;
case ams::svc::InfoType_TotalMemorySize:
*out = process->GetTotalUserPhysicalMemorySize();
break;
case ams::svc::InfoType_UsedMemorySize:
*out = process->GetUsedUserPhysicalMemorySize();
break;
case ams::svc::InfoType_AslrRegionAddress:
*out = GetInteger(process->GetPageTable().GetAliasCodeRegionStart());
break;
case ams::svc::InfoType_AslrRegionSize:
*out = process->GetPageTable().GetAliasCodeRegionSize();
break;
case ams::svc::InfoType_StackRegionAddress:
*out = GetInteger(process->GetPageTable().GetStackRegionStart());
break;
case ams::svc::InfoType_StackRegionSize:
*out = process->GetPageTable().GetStackRegionSize();
break;
case ams::svc::InfoType_SystemResourceSizeTotal:
*out = process->GetTotalSystemResourceSize();
break;
case ams::svc::InfoType_SystemResourceSizeUsed:
*out = process->GetUsedSystemResourceSize();
break;
case ams::svc::InfoType_ProgramId:
*out = process->GetProgramId();
break;
case ams::svc::InfoType_UserExceptionContextAddress:
*out = GetInteger(process->GetProcessLocalRegionAddress());
break;
case ams::svc::InfoType_TotalNonSystemMemorySize:
*out = process->GetTotalNonSystemUserPhysicalMemorySize();
break;
case ams::svc::InfoType_UsedNonSystemMemorySize:
*out = process->GetUsedNonSystemUserPhysicalMemorySize();
break;
case ams::svc::InfoType_IsApplication:
*out = process->IsApplication();
break;
case ams::svc::InfoType_FreeThreadCount:
if (KResourceLimit *resource_limit = process->GetResourceLimit(); resource_limit != nullptr) {
const auto current_value = resource_limit->GetCurrentValue(ams::svc::LimitableResource_ThreadCountMax);
const auto limit_value = resource_limit->GetLimitValue(ams::svc::LimitableResource_ThreadCountMax);
*out = limit_value - current_value;
} else {
*out = 0;
}
break;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
/* Return the relevant info. */
return GetInfoImpl(out, info_type, process.GetPointerUnsafe());
#endif
}
break;
case ams::svc::InfoType_DebuggerAttached:

View File

@@ -15,10 +15,10 @@
*/
#include <mesosphere.hpp>
namespace ams::kern::svc {
#pragma GCC push_options
#pragma GCC optimize ("-O3")
#pragma GCC push_options
#pragma GCC optimize ("-O3")
namespace ams::kern::svc {
/* ============================= Common ============================= */
@@ -315,6 +315,6 @@ namespace ams::kern::svc {
return ReplyAndReceiveWithUserBuffer(out_index, message_buffer, message_buffer_size, handles, num_handles, reply_target, timeout_ns);
}
#pragma GCC pop_options
}
#pragma GCC pop_options

View File

@@ -210,7 +210,7 @@ namespace ams::kern::svc {
KResourceLimit *process_resource_limit = resource_limit.IsNotNull() ? resource_limit.GetPointerUnsafe() : std::addressof(Kernel::GetSystemResourceLimit());
/* Get the pool for the process. */
const auto pool = [] ALWAYS_INLINE_LAMBDA (u32 flags) -> KMemoryManager::Pool {
const auto pool = [](u32 flags) ALWAYS_INLINE_LAMBDA -> KMemoryManager::Pool {
if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
switch (flags & ams::svc::CreateProcessFlag_PoolPartitionMask) {
case ams::svc::CreateProcessFlag_PoolPartitionApplication:

View File

@@ -63,6 +63,7 @@ namespace ams::impl {
AMS_DEFINE_SYSTEM_THREAD(-7, mitm, InitializeThread);
AMS_DEFINE_SYSTEM_THREAD(-1, mitm_sf, QueryServerProcessThread);
AMS_DEFINE_SYSTEM_THREAD(16, mitm_fs, RomFileSystemInitializeThread);
AMS_DEFINE_SYSTEM_THREAD(16, mitm_fs, RomFileSystemFinalizeThread);
AMS_DEFINE_SYSTEM_THREAD(21, mitm, DebugThrowThread);
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, IpcServer);
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, AsyncPrepareSdCardUpdateTask);

View File

@@ -118,12 +118,12 @@ namespace ams::fssystem {
/* Copy API. */
Result CopyFile(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size);
NX_INLINE Result CopyFile(fs::fsa::IFileSystem *fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size) {
ALWAYS_INLINE Result CopyFile(fs::fsa::IFileSystem *fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size) {
return CopyFile(fs, fs, dst_parent_path, src_path, dir_ent, work_buf, work_buf_size);
}
Result CopyDirectoryRecursively(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const char *dst_path, const char *src_path, void *work_buf, size_t work_buf_size);
NX_INLINE Result CopyDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *dst_path, const char *src_path, void *work_buf, size_t work_buf_size) {
ALWAYS_INLINE Result CopyDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *dst_path, const char *src_path, void *work_buf, size_t work_buf_size) {
return CopyDirectoryRecursively(fs, fs, dst_path, src_path, work_buf, work_buf_size);
}
@@ -148,11 +148,11 @@ namespace ams::fssystem {
Result EnsureDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path);
Result EnsureParentDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path);
template<typename F>
NX_INLINE Result RetryFinitelyForTargetLocked(F f) {
template<s64 RetryMilliSeconds = 100>
ALWAYS_INLINE Result RetryFinitelyForTargetLocked(auto f) {
/* Retry up to 10 times, 100ms between retries. */
constexpr s32 MaxRetryCount = 10;
constexpr TimeSpan RetryWaitTime = TimeSpan::FromMilliSeconds(100);
constexpr TimeSpan RetryWaitTime = TimeSpan::FromMilliSeconds(RetryMilliSeconds);
s32 remaining_retries = MaxRetryCount;
while (true) {

View File

@@ -66,6 +66,9 @@ namespace ams::hos {
Version_12_0_3 = ::ams::TargetFirmware_12_0_3,
Version_12_1_0 = ::ams::TargetFirmware_12_1_0,
Version_13_0_0 = ::ams::TargetFirmware_13_0_0,
Version_13_1_0 = ::ams::TargetFirmware_13_1_0,
Version_13_2_0 = ::ams::TargetFirmware_13_2_0,
Version_13_2_1 = ::ams::TargetFirmware_13_2_1,
Version_Current = ::ams::TargetFirmware_Current,

View File

@@ -62,20 +62,16 @@ namespace ams::settings {
char name[MaxLength];
static constexpr LanguageCode Encode(const char *name, size_t name_size) {
static constexpr LanguageCode Encode(util::string_view name) {
LanguageCode out{};
for (size_t i = 0; i < MaxLength && i < name_size; i++) {
for (size_t i = 0; i < MaxLength && i < name.size(); i++) {
out.name[i] = name[i];
}
return out;
}
static constexpr LanguageCode Encode(const char *name) {
return Encode(name, std::strlen(name));
}
template<Language Lang>
static constexpr inline LanguageCode EncodeLanguage = [] {
static constexpr inline LanguageCode EncodeLanguage() {
if constexpr (false) { /* ... */ }
#define AMS_MATCH_LANGUAGE(lang, enc) else if constexpr (Lang == Language_##lang) { return LanguageCode::Encode(enc); }
AMS_MATCH_LANGUAGE(Japanese, "ja")
@@ -98,28 +94,28 @@ namespace ams::settings {
AMS_MATCH_LANGUAGE(TraditionalChinese, "zh-Hant")
#undef AMS_MATCH_LANGUAGE
else { static_assert(Lang != Language_Japanese); }
}();
}
static constexpr inline LanguageCode Encode(const Language language) {
constexpr LanguageCode EncodedLanguages[Language_Count] = {
EncodeLanguage<Language_Japanese>,
EncodeLanguage<Language_AmericanEnglish>,
EncodeLanguage<Language_French>,
EncodeLanguage<Language_German>,
EncodeLanguage<Language_Italian>,
EncodeLanguage<Language_Spanish>,
EncodeLanguage<Language_Chinese>,
EncodeLanguage<Language_Korean>,
EncodeLanguage<Language_Dutch>,
EncodeLanguage<Language_Portuguese>,
EncodeLanguage<Language_Russian>,
EncodeLanguage<Language_Taiwanese>,
EncodeLanguage<Language_BritishEnglish>,
EncodeLanguage<Language_CanadianFrench>,
EncodeLanguage<Language_LatinAmericanSpanish>,
EncodeLanguage<Language_Japanese>(),
EncodeLanguage<Language_AmericanEnglish>(),
EncodeLanguage<Language_French>(),
EncodeLanguage<Language_German>(),
EncodeLanguage<Language_Italian>(),
EncodeLanguage<Language_Spanish>(),
EncodeLanguage<Language_Chinese>(),
EncodeLanguage<Language_Korean>(),
EncodeLanguage<Language_Dutch>(),
EncodeLanguage<Language_Portuguese>(),
EncodeLanguage<Language_Russian>(),
EncodeLanguage<Language_Taiwanese>(),
EncodeLanguage<Language_BritishEnglish>(),
EncodeLanguage<Language_CanadianFrench>(),
EncodeLanguage<Language_LatinAmericanSpanish>(),
/* 4.0.0+ */
EncodeLanguage<Language_SimplifiedChinese>,
EncodeLanguage<Language_TraditionalChinese>,
EncodeLanguage<Language_SimplifiedChinese>(),
EncodeLanguage<Language_TraditionalChinese>(),
};
return EncodedLanguages[language];
}

View File

@@ -54,11 +54,11 @@ namespace ams::sf::cmif {
void DisposeImpl();
virtual void AddReference() {
virtual void AddReference() override {
ServiceObjectImplBase2::AddReferenceImpl();
}
virtual void Release() {
virtual void Release() override {
if (ServiceObjectImplBase2::ReleaseImpl()) {
this->DisposeImpl();
}

View File

@@ -53,16 +53,32 @@ namespace ams::sf::cmif {
u32 cmd_id;
Result (*handler)(CmifOutHeader **out_header_ptr, ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data);
constexpr inline bool Matches(u32 cmd_id, hos::Version hosver) const {
constexpr inline bool MatchesVersion(hos::Version hosver) const {
const bool min_valid = this->hosver_low == hos::Version_Min;
const bool max_valid = this->hosver_high == hos::Version_Max;
return this->cmd_id == cmd_id && (min_valid || this->hosver_low <= hosver) && (max_valid || hosver <= this->hosver_high);
return (min_valid || this->hosver_low <= hosver) && (max_valid || hosver <= this->hosver_high);
}
constexpr inline bool Matches(u32 cmd_id, hos::Version hosver) const {
return this->cmd_id == cmd_id && this->MatchesVersion(hosver);
}
constexpr inline decltype(handler) GetHandler() const {
return this->handler;
}
constexpr inline bool operator>(const ServiceCommandMeta &rhs) const {
if (this->cmd_id > rhs.cmd_id) {
return true;
} else if (this->cmd_id == rhs.cmd_id && this->hosver_low > rhs.hosver_low) {
return true;
} else if (this->cmd_id == rhs.cmd_id && this->hosver_low == rhs.hosver_low && this->hosver_high == rhs.hosver_high){
return true;
} else {
return false;
}
}
};
static_assert(util::is_pod<ServiceCommandMeta>::value && sizeof(ServiceCommandMeta) == 0x18, "sizeof(ServiceCommandMeta)");

View File

@@ -85,16 +85,41 @@ namespace ams::sf::impl {
constexpr const auto &BaseEntries = ::ams::sf::cmif::ServiceDispatchTraits<BASECLASS>::DispatchTable.GetEntries(); \
constexpr size_t BaseSize = BaseEntries.size(); \
\
std::array<::ams::sf::cmif::ServiceCommandMeta, BaseSize + CurSize> combined_entries{}; \
for (size_t i = 0; i < BaseSize; ++i) { \
combined_entries[i] = BaseEntries[i]; \
constexpr size_t CombinedSize = BaseSize + CurSize; \
\
std::array<size_t, CombinedSize> map{}; \
for (size_t i = 0; i < CombinedSize; ++i) { map[i] = i; } \
\
for (size_t i = 1; i < CombinedSize; ++i) { \
size_t j = i; \
while (j > 0) { \
const auto li = map[j]; \
const auto ri = map[j - 1]; \
\
const auto &lhs = (li < BaseSize) ? BaseEntries[li] : cur_entries[li - BaseSize]; \
const auto &rhs = (ri < BaseSize) ? BaseEntries[ri] : cur_entries[ri - BaseSize]; \
\
if (!(rhs > lhs)) { \
break; \
} \
\
std::swap(map[j], map[j - 1]); \
\
--j; \
} \
} \
for (size_t i = 0; i < CurSize; ++i) { \
combined_entries[BaseSize + i] = cur_entries[i]; \
\
std::array<::ams::sf::cmif::ServiceCommandMeta, CombinedSize> combined_entries{}; \
for (size_t i = 0; i < CombinedSize; ++i) { \
if (map[i] < BaseSize) { \
combined_entries[i] = BaseEntries[map[i]]; \
} else { \
combined_entries[i] = cur_entries[map[i] - BaseSize]; \
} \
} \
\
return ::ams::sf::cmif::ServiceDispatchTable { combined_entries }; \
} () \
}() \
}; \
};

View File

@@ -43,7 +43,7 @@ namespace ams::sf {
return lmem::FreeToExpHeap(m_handle, buffer);
}
virtual bool IsEqualImpl(const MemoryResource &resource) const {
virtual bool IsEqualImpl(const MemoryResource &resource) const override {
return this == std::addressof(resource);
}
};
@@ -76,9 +76,9 @@ namespace ams::sf {
return lmem::FreeToUnitHeap(m_handle, buffer);
}
virtual bool IsEqualImpl(const MemoryResource &resource) const {
virtual bool IsEqualImpl(const MemoryResource &resource) const override {
return this == std::addressof(resource);
}
};
}
}

View File

@@ -37,9 +37,9 @@ namespace ams::sf {
return m_standard_allocator->Free(buffer);
}
virtual bool IsEqualImpl(const MemoryResource &resource) const {
virtual bool IsEqualImpl(const MemoryResource &resource) const override {
return this == std::addressof(resource);
}
};
}
}

View File

@@ -31,7 +31,7 @@ namespace ams::sf {
public:
class Object;
using Allocator = StatelessDummyAllocator;
using StatelessAllocator = typename Policy::StatelessAllocator<Object>;
using StatelessAllocator = typename Policy::template StatelessAllocator<Object>;
class Object final : private ::ams::sf::impl::ServiceObjectImplBase2, public Base {
NON_COPYABLE(Object);

View File

@@ -40,8 +40,8 @@ namespace ams::sm {
return out;
}
static constexpr ServiceName Encode(const char *name) {
return Encode(name, std::strlen(name));
static constexpr ServiceName Encode(util::string_view name) {
return Encode(name.data(), name.size());
}
};

View File

@@ -43,6 +43,7 @@ namespace ams::socket {
s32 Shutdown(s32 desc, ShutdownMethod how);
s32 Socket(Family domain, Type type, Protocol protocol);
s32 SocketExempt(Family domain, Type type, Protocol protocol);
s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len);

View File

@@ -57,4 +57,42 @@ namespace ams::socket {
}
};
class SystemConfigLightDefault : public Config {
public:
static constexpr size_t DefaultTcpInitialSendBufferSize = 16_KB;
static constexpr size_t DefaultTcpInitialReceiveBufferSize = 16_KB;
static constexpr size_t DefaultTcpAutoSendBufferSizeMax = 0_KB;
static constexpr size_t DefaultTcpAutoReceiveBufferSizeMax = 0_KB;
static constexpr size_t DefaultUdpSendBufferSize = 9_KB;
static constexpr size_t DefaultUdpReceiveBufferSize = 42240;
static constexpr auto DefaultSocketBufferEfficiency = 2;
static constexpr auto DefaultConcurrency = 2;
static constexpr size_t DefaultAllocatorPoolSize = 64_KB;
static constexpr size_t PerTcpSocketWorstCaseMemoryPoolSize = [] {
constexpr size_t WorstCaseTcpSendBufferSize = AlignMss(std::max(DefaultTcpInitialSendBufferSize, DefaultTcpAutoSendBufferSizeMax));
constexpr size_t WorstCaseTcpReceiveBufferSize = AlignMss(std::max(DefaultTcpInitialReceiveBufferSize, DefaultTcpAutoReceiveBufferSizeMax));
return util::AlignUp(WorstCaseTcpSendBufferSize * DefaultSocketBufferEfficiency + WorstCaseTcpReceiveBufferSize * DefaultSocketBufferEfficiency, os::MemoryPageSize);
}();
static constexpr size_t PerUdpSocketWorstCaseMemoryPoolSize = [] {
constexpr size_t WorstCaseUdpSendBufferSize = AlignMss(DefaultUdpSendBufferSize);
constexpr size_t WorstCaseUdpReceiveBufferSize = AlignMss(DefaultUdpReceiveBufferSize);
return util::AlignUp(WorstCaseUdpSendBufferSize * DefaultSocketBufferEfficiency + WorstCaseUdpReceiveBufferSize * DefaultSocketBufferEfficiency, os::MemoryPageSize);
}();
public:
constexpr SystemConfigLightDefault(void *mp, size_t mp_sz, size_t ap, int c=DefaultConcurrency)
: Config(mp, mp_sz, ap,
DefaultTcpInitialSendBufferSize, DefaultTcpInitialReceiveBufferSize,
DefaultTcpAutoSendBufferSizeMax, DefaultTcpAutoReceiveBufferSizeMax,
DefaultUdpSendBufferSize, DefaultUdpReceiveBufferSize,
DefaultSocketBufferEfficiency, c)
{
/* Mark as system. */
m_system = true;
}
};
}

View File

@@ -51,7 +51,7 @@ namespace ams::spl::impl {
Result AllocateAesKeySlot(s32 *out_keyslot);
Result DeallocateAesKeySlot(s32 keyslot);
Result TestAesKeySlot(s32 *out_index, s32 keyslot);
Result TestAesKeySlot(s32 *out_index, bool *out_virtual, s32 keyslot);
os::SystemEvent *GetAesKeySlotAvailableEvent();

View File

@@ -50,7 +50,7 @@ namespace ams::time {
AMS_ASSERT(rhs.IsValid());
}
constexpr auto ToUint64 = [] ALWAYS_INLINE_LAMBDA (const time::CalendarTime &time) {
constexpr auto ToUint64 = [](const time::CalendarTime &time) ALWAYS_INLINE_LAMBDA {
return (static_cast<u64>(time.year) << 40) |
(static_cast<u64>(time.month) << 32) |
(static_cast<u64>(time.day) << 24) |

View File

@@ -40,12 +40,28 @@ namespace ams::tipc::impl {
#define AMS_TIPC_IMPL_EXTRACT_SYNC_METHOD_ARGUMENTS(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
using NAME##ArgumentsType = ::ams::tipc::impl::SyncFunctionArgsType<&NAME##ArgumentsFunctionHolder::f>;
#define AMS_TIPC_IMPL_GET_MAXIMUM_REQUEST_SIZE(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
, ::ams::tipc::impl::CommandMetaInfo<CMD_ID + 0x10, NAME##ArgumentsType>::InMessageTotalSize
#define AMS_TIPC_IMPL_GET_MAXIMUM_RESPONSE_SIZE(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
, ::ams::tipc::impl::CommandMetaInfo<CMD_ID + 0x10, NAME##ArgumentsType>::OutMessageTotalSize
#define AMS_TIPC_IMPL_DEFINE_INTERFACE(BASECLASS, CLASSNAME, CMD_MACRO) \
class CLASSNAME : public BASECLASS { \
private: \
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_DEFINE_SYNC_METHOD_HOLDER) \
public: \
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_EXTRACT_SYNC_METHOD_ARGUMENTS) \
public: \
static constexpr size_t MaximumRequestSize = std::max<size_t>({ \
0 \
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_GET_MAXIMUM_REQUEST_SIZE) \
}); \
\
static constexpr size_t MaximumResponseSize = std::max<size_t>({ \
0 \
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_GET_MAXIMUM_RESPONSE_SIZE) \
}); \
};
#define AMS_TIPC_IMPL_DEFINE_CONCEPT_HELPERS(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \

View File

@@ -319,6 +319,9 @@ namespace ams::tipc::impl {
static constexpr auto OutMessageResultIndex = svc::ipc::MessageBuffer::GetRawDataIndex(OutMessageHeader, OutSpecialHeader);
static constexpr auto OutMessageRawDataIndex = OutMessageResultIndex + 1;
static constexpr size_t InMessageTotalSize = (InMessageRawDataIndex * sizeof(u32)) + InDataSize;
static constexpr size_t OutMessageTotalSize = (OutMessageRawDataIndex * sizeof(u32)) + OutDataSize;
/* Construction of argument serialization structs. */
private:
template<typename>
@@ -472,7 +475,7 @@ namespace ams::tipc::impl {
using OutRawHolderType = OutRawHolder<CommandMeta::OutDataSize, CommandMeta::OutDataAlign, CommandMeta::OutMessageRawDataIndex>;
using OutHandleHolderType = OutHandleHolder<CommandMeta::NumOutMoveHandles, CommandMeta::NumOutCopyHandles, CommandMeta::OutMessageHandleIndex>;
private:
static consteval u64 GetMessageHeaderForCheck(const svc::ipc::MessageBuffer::MessageHeader &header) {
static constexpr u64 GetMessageHeaderForCheck(const svc::ipc::MessageBuffer::MessageHeader &header) {
using Value = util::BitPack32::Field<0, BITSIZEOF(util::BitPack32)>;
const util::BitPack32 *data = header.GetData();
@@ -482,7 +485,7 @@ namespace ams::tipc::impl {
return static_cast<u64>(lower) | (static_cast<u64>(upper) << BITSIZEOF(u32));
}
static consteval u32 GetSpecialHeaderForCheck(const svc::ipc::MessageBuffer::SpecialHeader &header) {
static constexpr u32 GetSpecialHeaderForCheck(const svc::ipc::MessageBuffer::SpecialHeader &header) {
using Value = util::BitPack32::Field<0, BITSIZEOF(util::BitPack32)>;
return header.GetHeader()->Get<Value>();

View File

@@ -44,16 +44,17 @@ namespace ams::tipc {
}
class DeferrableBase : public impl::DeferrableBaseTag {
class DeferrableBaseImpl : public impl::DeferrableBaseTag {
private:
DeferralManagerBase *m_deferral_manager;
ObjectHolder m_object_holder;
uintptr_t m_resume_key;
u8 m_message_buffer[svc::ipc::MessageBufferSize];
const u32 m_message_buffer_size;
u8 m_message_buffer_base[0];
public:
ALWAYS_INLINE DeferrableBase() : m_deferral_manager(nullptr), m_object_holder(), m_resume_key() { /* ... */ }
ALWAYS_INLINE DeferrableBaseImpl(u32 mb_size) : m_deferral_manager(nullptr), m_object_holder(), m_resume_key(), m_message_buffer_size(mb_size) { /* ... */ }
~DeferrableBase();
~DeferrableBaseImpl();
ALWAYS_INLINE void SetDeferralManager(DeferralManagerBase *manager, os::NativeHandle reply_target, ServiceObjectBase *object) {
m_deferral_manager = manager;
@@ -67,7 +68,7 @@ namespace ams::tipc {
template<IsResumeKey ResumeKey>
ALWAYS_INLINE void RegisterRetry(ResumeKey key) {
m_resume_key = ConvertToInternalResumeKey(key);
std::memcpy(m_message_buffer, svc::ipc::GetMessageBuffer(), sizeof(m_message_buffer));
std::memcpy(m_message_buffer_base, svc::ipc::GetMessageBuffer(), m_message_buffer_size);
}
template<IsResumeKey ResumeKey, typename F>
@@ -85,11 +86,49 @@ namespace ams::tipc {
m_resume_key = 0;
/* Restore message buffer. */
std::memcpy(svc::ipc::GetMessageBuffer(), m_message_buffer, sizeof(m_message_buffer));
std::memcpy(svc::ipc::GetMessageBuffer(), m_message_buffer_base, m_message_buffer_size);
/* Process the request. */
return port_manager->ProcessDeferredRequest(m_object_holder);
}
protected:
static consteval size_t GetMessageBufferOffsetBase();
};
static_assert(std::is_standard_layout<DeferrableBaseImpl>::value);
template<size_t _MessageBufferRequiredSize>
class DeferrableBaseImplWithBuffer : public DeferrableBaseImpl {
private:
static constexpr size_t MessageBufferRequiredSize = _MessageBufferRequiredSize;
private:
u8 m_message_buffer[MessageBufferRequiredSize];
public:
DeferrableBaseImplWithBuffer();
private:
static consteval size_t GetMessageBufferOffset();
};
consteval size_t DeferrableBaseImpl::GetMessageBufferOffsetBase() {
return AMS_OFFSETOF(DeferrableBaseImpl, m_message_buffer_base);
}
template<size_t _MessageBufferRequiredSize>
consteval size_t DeferrableBaseImplWithBuffer<_MessageBufferRequiredSize>::GetMessageBufferOffset() {
return AMS_OFFSETOF(DeferrableBaseImplWithBuffer<_MessageBufferRequiredSize>, m_message_buffer);
}
template<size_t _MessageBufferRequiredSize>
ALWAYS_INLINE DeferrableBaseImplWithBuffer<_MessageBufferRequiredSize>::DeferrableBaseImplWithBuffer() : DeferrableBaseImpl(MessageBufferRequiredSize) {
static_assert(GetMessageBufferOffsetBase() == GetMessageBufferOffset());
static_assert(sizeof(DeferrableBaseImplWithBuffer<_MessageBufferRequiredSize>) >= sizeof(DeferrableBaseImpl) + MessageBufferRequiredSize);
}
template<typename Interface, size_t MaximumDefaultRequestSize = 0>
class DeferrableBase : public DeferrableBaseImplWithBuffer<std::max(Interface::MaximumRequestSize, MaximumDefaultRequestSize)> {
private:
using BaseImpl = DeferrableBaseImplWithBuffer<std::max(Interface::MaximumRequestSize, MaximumDefaultRequestSize)>;
public:
using BaseImpl::BaseImpl;
};
template<class T>
@@ -100,11 +139,11 @@ namespace ams::tipc {
NON_MOVEABLE(DeferralManagerBase);
private:
size_t m_object_count;
DeferrableBase *m_objects_base[0];
DeferrableBaseImpl *m_objects_base[0];
public:
ALWAYS_INLINE DeferralManagerBase() : m_object_count(0) { /* ... */ }
void AddObject(DeferrableBase &object, os::NativeHandle reply_target, ServiceObjectBase *service_object) {
void AddObject(DeferrableBaseImpl &object, os::NativeHandle reply_target, ServiceObjectBase *service_object) {
/* Set ourselves as the manager for the object. */
object.SetDeferralManager(this, reply_target, service_object);
@@ -113,7 +152,7 @@ namespace ams::tipc {
m_objects_base[m_object_count++] = std::addressof(object);
}
void RemoveObject(DeferrableBase *object) {
void RemoveObject(DeferrableBaseImpl *object) {
/* If the object is present, remove it. */
for (size_t i = 0; i < m_object_count; ++i) {
if (m_objects_base[i] == object) {
@@ -148,7 +187,7 @@ namespace ams::tipc {
};
static_assert(std::is_standard_layout<DeferralManagerBase>::value);
inline DeferrableBase::~DeferrableBase() {
inline DeferrableBaseImpl::~DeferrableBaseImpl() {
AMS_ASSUME(m_deferral_manager != nullptr);
m_deferral_manager->RemoveObject(this);
}
@@ -156,7 +195,7 @@ namespace ams::tipc {
template<size_t N> requires (N > 0)
class DeferralManager final : public DeferralManagerBase {
private:
DeferrableBase *m_objects[N];
DeferrableBaseImpl *m_objects[N];
public:
DeferralManager();
private:
@@ -167,15 +206,15 @@ namespace ams::tipc {
return AMS_OFFSETOF(DeferralManagerBase, m_objects_base);
}
template<size_t N>
template<size_t N> requires (N > 0)
consteval size_t DeferralManager<N>::GetObjectPointersOffset() {
return AMS_OFFSETOF(DeferralManager<N>, m_objects);
}
template<size_t N>
template<size_t N> requires (N > 0)
inline DeferralManager<N>::DeferralManager() : DeferralManagerBase() {
static_assert(GetObjectPointersOffset() == GetObjectPointersOffsetBase());
static_assert(sizeof(DeferralManager<N>) == sizeof(DeferralManagerBase) + N * sizeof(DeferrableBase *));
static_assert(sizeof(DeferralManager<N>) == sizeof(DeferralManagerBase) + N * sizeof(DeferrableBaseImpl *));
}
}

View File

@@ -686,7 +686,7 @@ namespace ams::tipc {
}
/* Try to reply/receive. */
const Result result = [&] ALWAYS_INLINE_LAMBDA () -> Result {
const Result result = [&]() ALWAYS_INLINE_LAMBDA -> Result {
os::MultiWaitHolderType *signaled_holder = nullptr;
ON_SCOPE_EXIT { AMS_ABORT_UNLESS(signaled_holder == nullptr); };
return m_object_manager->ReplyAndReceive(std::addressof(signaled_holder), out_object, reply_target, std::addressof(m_multi_wait));
@@ -747,7 +747,7 @@ namespace ams::tipc {
using PortManager = PortManagerImpl;
private:
PortManager m_port_manager;
PortInfo::Allocator m_port_allocator;
typename PortInfo::Allocator m_port_allocator;
public:
constexpr ServerManagerImpl() : m_port_manager(), m_port_allocator() { /* ... */ }

View File

@@ -18,41 +18,28 @@
#include <vapours.hpp>
#include <stratosphere/os/os_sdk_mutex.hpp>
#define AMS_SINGLETON_TRAITS(_CLASSNAME_) \
private: \
NON_COPYABLE(_CLASSNAME_); \
NON_MOVEABLE(_CLASSNAME_); \
private: \
_CLASSNAME_ (); \
public: \
static _CLASSNAME_ &GetInstance() { \
/* Declare singleton instance variables. */ \
static constinit ::ams::util::TypedStorage<_CLASSNAME_> s_singleton_storage; \
static constinit ::ams::os::SdkMutex s_singleton_mutex; \
static constinit bool s_initialized_singleton = false; \
\
/* Ensure the instance is created. */ \
if (AMS_UNLIKELY(!s_initialized_singleton)) { \
std::scoped_lock lk(s_singleton_mutex); \
\
if (AMS_LIKELY(!s_initialized_singleton)) { \
new (::ams::util::GetPointer(s_singleton_storage)) _CLASSNAME_; \
s_initialized_singleton = true; \
} \
} \
\
return ::ams::util::GetReference(s_singleton_storage); \
#define AMS_SINGLETON_TRAITS(_CLASSNAME_) \
private: \
NON_COPYABLE(_CLASSNAME_); \
NON_MOVEABLE(_CLASSNAME_); \
private: \
_CLASSNAME_ (); \
public: \
static _CLASSNAME_ &GetInstance() { \
AMS_FUNCTION_LOCAL_STATIC(_CLASSNAME_, s_singleton_instance); \
\
return s_singleton_instance; \
}
#define AMS_CONSTINIT_SINGLETON_TRAITS(_CLASSNAME_) \
private: \
NON_COPYABLE(_CLASSNAME_); \
NON_MOVEABLE(_CLASSNAME_); \
private: \
constexpr _CLASSNAME_ () = default; \
public: \
static _CLASSNAME_ &GetInstance() { \
/* Declare singleton instance variables. */ \
static constinit _CLASSNAME_ s_singleton_instance; \
return s_singleton_instance; \
#define AMS_CONSTINIT_SINGLETON_TRAITS(_CLASSNAME_) \
private: \
NON_COPYABLE(_CLASSNAME_); \
NON_MOVEABLE(_CLASSNAME_); \
private: \
constexpr _CLASSNAME_ () = default; \
public: \
static _CLASSNAME_ &GetInstance() { \
/* Declare singleton instance variables. */ \
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(_CLASSNAME_, s_singleton_instance); \
return s_singleton_instance; \
}

View File

@@ -188,6 +188,12 @@ namespace ams::boot2 {
return enable_htc != 0;
}
bool IsStandaloneGdbstubEnabled() {
u8 enable_gdbstub = 0;
settings::fwdbg::GetSettingsItemValue(std::addressof(enable_gdbstub), sizeof(enable_gdbstub), "atmosphere", "enable_standalone_gdbstub");
return enable_gdbstub != 0;
}
bool IsAtmosphereLogManagerEnabled() {
/* If htc is enabled, ams log manager is enabled. */
if (IsHtcEnabled()) {
@@ -403,6 +409,9 @@ namespace ams::boot2 {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Htc, ncm::StorageId::None), 0);
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Cs, ncm::StorageId::None), 0);
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::DmntGen2, ncm::StorageId::None), 0);
} else if (IsStandaloneGdbstubEnabled()) {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::DmntGen2, ncm::StorageId::None), 0);
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::BuiltInSystem), 0);
} else {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Dmnt, ncm::StorageId::None), 0);
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::BuiltInSystem), 0);

View File

@@ -73,18 +73,7 @@ namespace ams::erpt::srv {
}
bool IsProductionMode() {
static constinit bool s_initialized = false;
static constinit bool s_is_prod_mode = true;
static constinit os::SdkMutex s_mutex;
if (AMS_UNLIKELY(!s_initialized)) {
std::scoped_lock lk(s_mutex);
if (AMS_LIKELY(!s_initialized)) {
s_is_prod_mode = IsProductionModeImpl();
s_initialized = true;
}
}
AMS_FUNCTION_LOCAL_STATIC(bool, s_is_prod_mode, IsProductionModeImpl());
return s_is_prod_mode;
}

View File

@@ -112,13 +112,13 @@ namespace ams::fssystem {
NcaCryptoConfiguration g_nca_crypto_configuration_prod;
constexpr inline s32 KeySlotCacheEntryCount = 3;
KeySlotCache g_key_slot_cache;
util::optional<KeySlotCacheEntry> g_key_slot_cache_entry[KeySlotCacheEntryCount];
constinit KeySlotCache g_key_slot_cache;
constinit util::optional<KeySlotCacheEntry> g_key_slot_cache_entry[KeySlotCacheEntryCount];
spl::AccessKey &GetNcaKekAccessKey(s32 key_type) {
static spl::AccessKey s_nca_kek_access_key_array[KeyAreaEncryptionKeyCount] = {};
static spl::AccessKey s_nca_header_kek_access_key = {};
static spl::AccessKey s_invalid_nca_kek_access_key = {};
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(spl::AccessKey, s_nca_kek_access_key_array[KeyAreaEncryptionKeyCount]);
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(spl::AccessKey, s_nca_header_kek_access_key);
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(spl::AccessKey, s_invalid_nca_kek_access_key);
if (key_type > static_cast<s32>(KeyType::NcaHeaderKey) || IsInvalidKeyTypeValue(key_type)) {
return s_invalid_nca_kek_access_key;

View File

@@ -26,42 +26,20 @@ namespace ams::gpio::driver::impl {
alignas(os::MemoryPageSize) u8 g_interrupt_thread_stack[InterruptThreadStackSize];
gpio::driver::IGpioDriver::List &GetGpioDriverList() {
static constinit gpio::driver::IGpioDriver::List s_gpio_driver_list;
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(gpio::driver::IGpioDriver::List, s_gpio_driver_list);
return s_gpio_driver_list;
}
ddsf::EventHandlerManager &GetInterruptHandlerManager() {
static constinit util::TypedStorage<ddsf::EventHandlerManager> s_interrupt_handler_manager;
static constinit bool s_initialized = false;
static constinit os::SdkMutex s_mutex;
AMS_FUNCTION_LOCAL_STATIC(ddsf::EventHandlerManager, s_interrupt_handler_manager);
if (AMS_UNLIKELY(!s_initialized)) {
std::scoped_lock lk(s_mutex);
if (AMS_LIKELY(!s_initialized)) {
util::ConstructAt(s_interrupt_handler_manager);
s_initialized = true;
}
}
return util::GetReference(s_interrupt_handler_manager);
return s_interrupt_handler_manager;
}
ddsf::DeviceCodeEntryManager &GetDeviceCodeEntryManager() {
static constinit util::TypedStorage<ddsf::DeviceCodeEntryManager> s_device_code_entry_manager;
static constinit bool s_initialized = false;
static constinit os::SdkMutex s_mutex;
AMS_FUNCTION_LOCAL_STATIC(ddsf::DeviceCodeEntryManager, s_device_code_entry_manager, ddsf::GetDeviceCodeEntryHolderMemoryResource());
if (AMS_UNLIKELY(!s_initialized)) {
std::scoped_lock lk(s_mutex);
if (AMS_LIKELY(!s_initialized)) {
util::ConstructAt(s_device_code_entry_manager, ddsf::GetDeviceCodeEntryHolderMemoryResource());
s_initialized = true;
}
}
return util::GetReference(s_device_code_entry_manager);
return s_device_code_entry_manager;
}
void InterruptThreadFunction(void *arg) {

View File

@@ -196,7 +196,7 @@ namespace ams::htclow::ctrl {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
auto IsSupportedServiceChannel = [] ALWAYS_INLINE_LAMBDA (const impl::ChannelInternalType &channel, const impl::ChannelInternalType *supported, int num_supported) -> bool {
auto IsSupportedServiceChannel = [](const impl::ChannelInternalType &channel, const impl::ChannelInternalType *supported, int num_supported) ALWAYS_INLINE_LAMBDA -> bool {
for (auto i = 0; i < num_supported; ++i) {
if (channel.module_id == supported[i].module_id && channel.channel_id == supported[i].channel_id) {
return true;

View File

@@ -95,16 +95,17 @@ namespace ams::i2c::driver::board::nintendo::nx {
}
}
constinit util::TypedStorage<impl::I2cBusAccessorManager> g_bus_accessor_manager;
constinit util::TypedStorage<impl::I2cDevicePropertyManager> g_device_manager;
}
void Initialize() {
static constinit util::TypedStorage<impl::I2cBusAccessorManager> s_bus_accessor_manager;
static constinit util::TypedStorage<impl::I2cDevicePropertyManager> s_device_manager;
/* Initialize managers. */
util::ConstructAt(g_bus_accessor_manager, ddsf::GetMemoryResource());
util::ConstructAt(g_device_manager, ddsf::GetMemoryResource());
util::ConstructAt(s_bus_accessor_manager, ddsf::GetMemoryResource());
util::ConstructAt(s_device_manager, ddsf::GetMemoryResource());
return Initialize(util::GetReference(s_bus_accessor_manager), util::GetReference(s_device_manager));
return Initialize(util::GetReference(g_bus_accessor_manager), util::GetReference(g_device_manager));
}
}

View File

@@ -24,25 +24,14 @@ namespace ams::i2c::driver::impl {
constinit int g_init_count = 0;
i2c::driver::II2cDriver::List &GetI2cDriverList() {
static constinit i2c::driver::II2cDriver::List s_driver_list;
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(i2c::driver::II2cDriver::List, s_driver_list);
return s_driver_list;
}
ddsf::DeviceCodeEntryManager &GetDeviceCodeEntryManager() {
static constinit util::TypedStorage<ddsf::DeviceCodeEntryManager> s_device_code_entry_manager;
static constinit bool s_initialized = false;
static constinit os::SdkMutex s_mutex;
AMS_FUNCTION_LOCAL_STATIC(ddsf::DeviceCodeEntryManager, s_device_code_entry_manager, ddsf::GetDeviceCodeEntryHolderMemoryResource());
if (AMS_UNLIKELY(!s_initialized)) {
std::scoped_lock lk(s_mutex);
if (AMS_LIKELY(!s_initialized)) {
util::ConstructAt(s_device_code_entry_manager, ddsf::GetDeviceCodeEntryHolderMemoryResource());
s_initialized = true;
}
}
return util::GetReference(s_device_code_entry_manager);
return s_device_code_entry_manager;
}
}

View File

@@ -34,7 +34,8 @@ namespace ams::lm::srv {
}
EventLogTransmitter &EventLogTransmitter::GetDefaultInstance() {
static constinit EventLogTransmitter s_default_event_log_transmitter(DefaultFlushFunction);
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(EventLogTransmitter, s_default_event_log_transmitter, DefaultFlushFunction);
return s_default_event_log_transmitter;
}

View File

@@ -63,8 +63,10 @@ namespace ams::lm::srv {
}
bool DefaultFlushFunction(const u8 *data, size_t size) {
/* Declare persistent clock-updated state storage. */
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_is_user_system_clock_updated, false);
/* Update clock. */
static constinit bool s_is_user_system_clock_updated = false;
if (!s_is_user_system_clock_updated) {
UpdateUserSystemClock(data, size);
s_is_user_system_clock_updated = true;
@@ -84,8 +86,8 @@ namespace ams::lm::srv {
}
LogBuffer &LogBuffer::GetDefaultInstance() {
static constinit u8 s_default_buffers[128_KB * 2];
static constinit LogBuffer s_default_log_buffer(s_default_buffers, sizeof(s_default_buffers), DefaultFlushFunction);
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(u8, s_default_buffers[128_KB * 2]);
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(LogBuffer, s_default_log_buffer, s_default_buffers, sizeof(s_default_buffers), DefaultFlushFunction);
return s_default_log_buffer;
}

View File

@@ -19,8 +19,9 @@
namespace ams::lm::srv {
CustomSinkBuffer &LogGetterImpl::GetBuffer() {
static constinit u8 s_buffer[32_KB];
static constinit CustomSinkBuffer s_custom_sink_buffer(s_buffer, sizeof(s_buffer), FlushFunction);
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(u8, s_buffer[32_KB]);
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(CustomSinkBuffer, s_custom_sink_buffer, s_buffer, sizeof(s_buffer), FlushFunction);
return s_custom_sink_buffer;
}

View File

@@ -188,7 +188,7 @@ namespace ams::lm::srv {
size_t carry_size;
bool ends_with_text_log;
};
static constinit PreviousPacketContext s_previous_packet_context = {};
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(PreviousPacketContext, s_previous_packet_context);
/* Get the packet header. */
auto *header = static_cast<const impl::LogPacketHeader *>(buffer);

View File

@@ -308,7 +308,7 @@ namespace ams::ncm {
out_orphaned[i] = true;
}
auto IsOrphanedContent = [] ALWAYS_INLINE_LAMBDA (const sf::InArray<ContentId> &list, const ncm::ContentId &id) -> util::optional<size_t> {
auto IsOrphanedContent = [](const sf::InArray<ContentId> &list, const ncm::ContentId &id) ALWAYS_INLINE_LAMBDA -> util::optional<size_t> {
/* Check if any input content ids match our found content id. */
for (size_t i = 0; i < list.GetSize(); i++) {
if (list[i] == id) {

View File

@@ -635,7 +635,7 @@ namespace ams::ncm {
for (s32 i = 0; i < count; i++) {
R_UNLESS(!this->IsCancelRequested(), ncm::ResultCreatePlaceHolderCancelled());
static constinit os::SdkMutex s_placeholder_mutex;
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(os::SdkMutex, s_placeholder_mutex);
std::scoped_lock lk(s_placeholder_mutex);
InstallContentMeta content_meta;

View File

@@ -44,7 +44,8 @@ namespace ams::ncm {
}
HeapState &GetHeapState() {
static HeapState s_heap_state = {};
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(HeapState, s_heap_state);
return s_heap_state;
}

View File

@@ -52,6 +52,26 @@ namespace ams::os::impl {
/* Get the thread impl object from libnx. */
ThreadImpl *thread_impl = ::threadGetSelf();
/* Hack around libnx's main thread, to ensure stratosphere thread type consistency. */
{
auto *tlr = reinterpret_cast<uintptr_t *>(svc::GetThreadLocalRegion());
for (size_t i = sizeof(svc::ThreadLocalRegion) / sizeof(uintptr_t); i > 0; --i) {
if (auto *candidate = reinterpret_cast<ThreadImpl *>(tlr[i - 1]); candidate == thread_impl) {
ThreadImpl *embedded_thread = std::addressof(main_thread->thread_impl_storage);
*embedded_thread = *thread_impl;
if (embedded_thread->next) {
embedded_thread->next->prev_next = std::addressof(embedded_thread->next);
}
thread_impl = embedded_thread;
tlr[i-1] = reinterpret_cast<uintptr_t>(thread_impl);
break;
}
}
}
/* Get the thread priority. */
s32 horizon_priority;
R_ABORT_UNLESS(svc::GetThreadPriority(std::addressof(horizon_priority), thread_impl->handle));

View File

@@ -54,7 +54,7 @@ namespace ams::osdbg::impl {
static_assert(AMS_OFFSETOF(ThreadLocalRegionIlp32, tls) == 0x1C0);
struct LibnxThreadVars {
static constexpr u32 Magic = util::FourCC<'!','T','V','$'>::Code;
static constexpr u32 Magic = util::ReverseFourCC<'!','T','V','$'>::Code;
u32 magic;
::Handle handle;
@@ -69,11 +69,11 @@ namespace ams::osdbg::impl {
volatile u16 disable_counter;
volatile u16 interrupt_flag;
u32 reserved0;
u64 tls[(0x1E0 - 0x108) / sizeof(u64)];
u64 tls[(0x200 - sizeof(LibnxThreadVars) - 0x108) / sizeof(u64)];
LibnxThreadVars thread_vars;
};
static_assert(sizeof(ThreadLocalRegionLibnx) == sizeof(svc::ThreadLocalRegion));
static_assert(AMS_OFFSETOF(ThreadLocalRegionLibnx, thread_vars) == 0x1E0);
static_assert(AMS_OFFSETOF(ThreadLocalRegionLibnx, thread_vars) == 0x200 - sizeof(LibnxThreadVars));
struct LibnxThreadEntryArgs {
u64 t;

View File

@@ -50,9 +50,9 @@ namespace ams::osdbg {
} else {
/* Special-case libnx threads. */
if (thread_info->_thread_type_type == ThreadTypeType_Libnx) {
util::TSNPrintf(dst, os::ThreadNameLengthMax, "libnx Thread_0x%p", reinterpret_cast<void *>(thread_info->_thread_type));
util::TSNPrintf(dst, os::ThreadNameLengthMax, "libnx Thread_0x%010lx", reinterpret_cast<uintptr_t>(thread_info->_thread_type));
} else {
util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_0x%p", reinterpret_cast<void *>(thread_info->_thread_type));
util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_0x%010lx", reinterpret_cast<uintptr_t>(thread_info->_thread_type));
}
return ResultSuccess();

View File

@@ -20,60 +20,44 @@ namespace ams::powctl::impl {
namespace {
os::ThreadType g_interrupt_thread;
constinit os::ThreadType g_interrupt_thread;
constexpr inline size_t InterruptThreadStackSize = os::MemoryPageSize;
alignas(os::MemoryPageSize) u8 g_interrupt_thread_stack[InterruptThreadStackSize];
constinit u8 g_unit_heap_memory[2_KB];
constinit lmem::HeapHandle g_unit_heap_handle;
constinit sf::UnitHeapMemoryResource g_unit_heap_memory_resource;
IPowerControlDriver::List &GetDriverList() {
static constinit IPowerControlDriver::List s_driver_list;
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(IPowerControlDriver::List, s_driver_list);
return s_driver_list;
}
ddsf::EventHandlerManager &GetInterruptHandlerManager() {
static constinit util::TypedStorage<ddsf::EventHandlerManager> s_interrupt_handler_manager;
static constinit bool s_initialized = false;
static constinit os::SdkMutex s_mutex;
AMS_FUNCTION_LOCAL_STATIC(ddsf::EventHandlerManager, s_interrupt_handler_manager);
if (AMS_UNLIKELY(!s_initialized)) {
std::scoped_lock lk(s_mutex);
if (AMS_LIKELY(!s_initialized)) {
util::ConstructAt(s_interrupt_handler_manager);
s_initialized = true;
}
}
return util::GetReference(s_interrupt_handler_manager);
return s_interrupt_handler_manager;
}
ddsf::DeviceCodeEntryManager &GetDeviceCodeEntryManager() {
static constinit util::TypedStorage<ddsf::DeviceCodeEntryManager> s_device_code_entry_manager;
static constinit bool s_initialized = false;
static constinit os::SdkMutex s_mutex;
class DeviceCodeEntryManagerWithUnitHeap {
private:
u8 m_heap_memory[2_KB];
sf::UnitHeapMemoryResource m_memory_resource;
util::TypedStorage<ddsf::DeviceCodeEntryManager> m_manager;
public:
DeviceCodeEntryManagerWithUnitHeap() {
/* Initialize the memory resource. */
m_memory_resource.Attach(lmem::CreateUnitHeap(m_heap_memory, sizeof(m_heap_memory), sizeof(ddsf::DeviceCodeEntryHolder), lmem::CreateOption_ThreadSafe));
if (AMS_UNLIKELY(!s_initialized)) {
std::scoped_lock lk(s_mutex);
/* Construct the entry manager. */
util::ConstructAt(m_manager, std::addressof(m_memory_resource));
}
if (AMS_LIKELY(!s_initialized)) {
/* Initialize the entry code heap. */
g_unit_heap_handle = lmem::CreateUnitHeap(g_unit_heap_memory, sizeof(g_unit_heap_memory), sizeof(ddsf::DeviceCodeEntryHolder), lmem::CreateOption_ThreadSafe);
ALWAYS_INLINE operator ddsf::DeviceCodeEntryManager &() {
return util::GetReference(m_manager);
}
};
AMS_FUNCTION_LOCAL_STATIC(DeviceCodeEntryManagerWithUnitHeap, s_device_code_entry_manager_holder);
/* Initialize the entry code memory resource. */
g_unit_heap_memory_resource.Attach(g_unit_heap_handle);
/* Make the entry manager using the newly initialized memory resource. */
util::ConstructAt(s_device_code_entry_manager, std::addressof(g_unit_heap_memory_resource));
s_initialized = true;
}
}
return util::GetReference(s_device_code_entry_manager);
return s_device_code_entry_manager_holder;
}
void InterruptThreadFunction(void *arg) {

View File

@@ -24,25 +24,14 @@ namespace ams::pwm::driver::impl {
constinit int g_init_count = 0;
pwm::driver::IPwmDriver::List &GetPwmDriverList() {
static constinit pwm::driver::IPwmDriver::List s_driver_list;
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(pwm::driver::IPwmDriver::List, s_driver_list);
return s_driver_list;
}
ddsf::DeviceCodeEntryManager &GetDeviceCodeEntryManager() {
static constinit util::TypedStorage<ddsf::DeviceCodeEntryManager> s_device_code_entry_manager;
static constinit bool s_initialized = false;
static constinit os::SdkMutex s_mutex;
AMS_FUNCTION_LOCAL_STATIC(ddsf::DeviceCodeEntryManager, s_device_code_entry_manager, ddsf::GetDeviceCodeEntryHolderMemoryResource());
if (AMS_UNLIKELY(!s_initialized)) {
std::scoped_lock lk(s_mutex);
if (AMS_LIKELY(!s_initialized)) {
util::ConstructAt(s_device_code_entry_manager, ddsf::GetDeviceCodeEntryHolderMemoryResource());
s_initialized = true;
}
}
return util::GetReference(s_device_code_entry_manager);
return s_device_code_entry_manager;
}
}

View File

@@ -289,14 +289,8 @@ namespace ams::settings::impl {
}
lmem::HeapHandle &GetHeapHandle() {
static constinit bool s_is_initialized = false;
static constinit lmem::HeapHandle s_heap_handle;
static constinit u8 s_heap_memory[HeapMemorySize];
if (!s_is_initialized) {
s_heap_handle = lmem::CreateExpHeap(s_heap_memory, sizeof(s_heap_memory), lmem::CreateOption_None);
s_is_initialized = true;
}
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(u8, s_heap_memory[HeapMemorySize]);
AMS_FUNCTION_LOCAL_STATIC(lmem::HeapHandle, s_heap_handle, lmem::CreateExpHeap(s_heap_memory, sizeof(s_heap_memory), lmem::CreateOption_ThreadSafe));
return s_heap_handle;
}
@@ -314,24 +308,15 @@ namespace ams::settings::impl {
AMS_ASSERT(out != nullptr);
/* Declare static instance variables. */
static constinit util::TypedStorage<Map> s_storage = {};
static constinit bool s_is_initialized = false;
static constinit bool s_is_loaded = false;
AMS_FUNCTION_LOCAL_STATIC(Map, s_map);
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_is_map_loaded, false);
/* Get pointer to the map. */
Map *map = util::GetPointer(s_storage);
/* Construct the map, if we haven't already. */
if (AMS_UNLIKELY(!s_is_initialized)) {
/* Construct the instance. */
util::ConstructAt(s_storage);
/* Note that we constructed. */
s_is_initialized = true;
}
Map * const map = std::addressof(s_map);
/* TODO: Mutex? */
/* Load the map, if we haven't already. */
if (AMS_UNLIKELY(!s_is_loaded)) {
if (AMS_UNLIKELY(!s_is_map_loaded)) {
/* Attempt to load the map, allowing for failure if acceptable. */
const auto result = LoadKeyValueStoreMap(map);
@@ -340,7 +325,7 @@ namespace ams::settings::impl {
}
/* Note that the map is loaded. */
s_is_loaded = true;
s_is_map_loaded = true;
}
/* Set the output pointer. */
@@ -425,26 +410,13 @@ namespace ams::settings::impl {
AMS_ASSERT(out_data != nullptr);
/* Declare static instance variables. */
static constinit util::TypedStorage<SystemData> s_storage = {};
static constinit bool s_initialized = false;
static constinit bool s_mounted = false;
AMS_FUNCTION_LOCAL_STATIC(SystemData, s_data, id, GetSystemDataMountName<T>());
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_mounted, false);
/* Get pointer to the system data. */
SystemData *data = util::GetPointer(s_storage);
/* Construct the system data, if we haven't already. */
if (AMS_UNLIKELY(!s_initialized)) {
/* Construct the instance. */
util::ConstructAt(s_storage);
/* Setup system data. */
data->SetSystemDataId(id);
data->SetMountName(GetSystemDataMountName<T>());
/* Note that we constructed. */
s_initialized = true;
}
SystemData *data = std::addressof(s_data);
/* TODO: Mutex? */
/* Mount the system data, if we haven't already. */
if (AMS_UNLIKELY(!s_mounted)) {
/* Mount the system data. */
@@ -464,28 +436,11 @@ namespace ams::settings::impl {
AMS_ASSERT(out_data != nullptr);
/* Declare static instance variables. */
static constinit util::TypedStorage<SystemSaveData> s_storage = {};
static constinit bool s_initialized = false;
static constinit bool s_mounted = false;
AMS_FUNCTION_LOCAL_STATIC(SystemSaveData, s_data, SystemSaveDataId, SystemSaveDataSize, SystemSaveDataJournalSize, SystemSaveDataFlags, SystemSaveDataMountName);
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_mounted, false);
/* Get pointer to the system data. */
SystemSaveData *data = util::GetPointer(s_storage);
/* Construct the system data, if we haven't already. */
if (AMS_UNLIKELY(!s_initialized)) {
/* Construct the instance. */
util::ConstructAt(s_storage);
/* Setup system data. */
data->SetSystemSaveDataId(SystemSaveDataId);
data->SetTotalSize(SystemSaveDataSize);
data->SetJournalSize(SystemSaveDataJournalSize);
data->SetFlags(SystemSaveDataFlags);
data->SetMountName(SystemSaveDataMountName);
/* Note that we constructed. */
s_initialized = true;
}
/* Get pointer to the system save data. */
SystemSaveData *data = std::addressof(s_data);
/* Mount the system data, if we haven't already. */
if (AMS_UNLIKELY(!s_mounted)) {

View File

@@ -56,27 +56,33 @@ namespace ams::settings::impl {
}
SplConfig GetSplConfig() {
static constinit bool s_is_initialized = false;
static constinit SplConfig s_config;
class SplConfigHolder {
NON_COPYABLE(SplConfigHolder);
NON_MOVEABLE(SplConfigHolder);
private:
SplConfig m_config;
public:
SplConfigHolder() {
/* Initialize spl. */
spl::Initialize();
ON_SCOPE_EXIT { spl::Finalize(); };
if (!s_is_initialized) {
/* Initialize spl. */
spl::Initialize();
ON_SCOPE_EXIT { spl::Finalize(); };
/* Create the config. */
m_config = {
.is_development = spl::IsDevelopment(),
.hardware_type = ConvertToSplHardwareType(spl::GetHardwareType()),
.is_quest = IsSplRetailInteractiveDisplayStateEnabled(spl::GetRetailInteractiveDisplayState()),
.device_id_low = spl::GetDeviceIdLow(),
};
}
/* Create the config. */
s_config = {
.is_development = spl::IsDevelopment(),
.hardware_type = ConvertToSplHardwareType(spl::GetHardwareType()),
.is_quest = IsSplRetailInteractiveDisplayStateEnabled(spl::GetRetailInteractiveDisplayState()),
.device_id_low = spl::GetDeviceIdLow(),
};
ALWAYS_INLINE operator SplConfig() {
return m_config;
}
};
/* Mark as initialized. */
s_is_initialized = true;
}
return s_config;
AMS_FUNCTION_LOCAL_STATIC(SplConfigHolder, s_config_holder);
return s_config_holder;
}
}

View File

@@ -25,27 +25,9 @@ namespace ams::settings::impl {
StaticObject();
public:
static T &Get() {
/* Declare static instance variables. */
static constinit util::TypedStorage<T> s_storage = {};
static constinit bool s_initialized = false;
static constinit os::SdkMutex s_mutex;
AMS_FUNCTION_LOCAL_STATIC(T, s_object);
/* If we haven't already done so, construct the instance. */
if (AMS_UNLIKELY(!s_initialized)) {
std::scoped_lock lk(s_mutex);
/* Check that we didn't concurrently construct the instance. */
if (AMS_LIKELY(!s_initialized)) {
/* Construct the instance. */
util::ConstructAt(s_storage);
/* Note that we constructed. */
s_initialized = true;
}
}
/* Return the constructed instance. */
return util::GetReference(s_storage);
return s_object;
}
};

View File

@@ -30,6 +30,11 @@ namespace ams::settings::impl {
public:
SystemData() : m_system_data_id(), m_mount_name(), m_file_path() { /* ... */ }
SystemData(ncm::SystemDataId id, const char *mn) : SystemData() {
this->SetSystemDataId(id);
this->SetMountName(mn);
}
void SetSystemDataId(ncm::SystemDataId id);
void SetMountName(const char *name);
Result Mount();

View File

@@ -30,6 +30,14 @@ namespace ams::settings::impl {
public:
SystemSaveData() : m_system_save_data_id(0), m_save_data_space_id(fs::SaveDataSpaceId::System), m_total_size(0), m_journal_size(0), m_flags(0) { /* ... */ }
SystemSaveData(u64 id, s64 total_size, s64 journal_size, u32 flags, const char *mn) : SystemSaveData() {
this->SetSystemSaveDataId(id);
this->SetTotalSize(total_size);
this->SetJournalSize(journal_size);
this->SetFlags(flags);
this->SetMountName(mn);
}
void SetSystemSaveDataId(u64 id);
void SetTotalSize(s64 size);
void SetJournalSize(s64 size);

View File

@@ -17,6 +17,65 @@
namespace ams::sf::cmif {
namespace {
ALWAYS_INLINE decltype(ServiceCommandMeta::handler) FindCommandHandlerByBinarySearch(const ServiceCommandMeta *entries, const size_t entry_count, const u32 cmd_id, const hos::Version hos_version) {
/* Binary search for the handler. */
ssize_t lo = 0;
ssize_t hi = entry_count - 1;
while (lo <= hi) {
const size_t mid = (lo + hi) / 2;
if (entries[mid].cmd_id < cmd_id) {
lo = mid + 1;
} else if (entries[mid].cmd_id > cmd_id) {
hi = mid - 1;
} else {
/* Find start. */
size_t start = mid;
while (start > 0 && entries[start - 1].cmd_id == cmd_id) {
--start;
}
/* Find end. */
size_t end = mid + 1;
while (end < entry_count && entries[end].cmd_id == cmd_id) {
++end;
}
for (size_t idx = start; idx < end; ++idx) {
if (entries[idx].MatchesVersion(hos_version)) {
return entries[idx].GetHandler();
}
}
break;
}
}
return nullptr;
}
ALWAYS_INLINE decltype(ServiceCommandMeta::handler) FindCommandHandlerByLinearSearch(const ServiceCommandMeta *entries, const size_t entry_count, const u32 cmd_id, const hos::Version hos_version) {
for (size_t i = 0; i < entry_count; ++i) {
if (entries[i].Matches(cmd_id, hos_version)) {
return entries[i].GetHandler();
break;
}
}
return nullptr;
}
ALWAYS_INLINE decltype(ServiceCommandMeta::handler) FindCommandHandler(const ServiceCommandMeta *entries, const size_t entry_count, const u32 cmd_id, const hos::Version hos_version) {
if (entry_count >= 8) {
return FindCommandHandlerByBinarySearch(entries, entry_count, cmd_id, hos_version);
} else {
return FindCommandHandlerByLinearSearch(entries, entry_count, cmd_id, hos_version);
}
}
}
Result impl::ServiceDispatchTableBase::ProcessMessageImpl(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const ServiceCommandMeta *entries, const size_t entry_count) const {
/* Get versioning info. */
const auto hos_version = hos::GetVersion();
@@ -30,13 +89,7 @@ namespace ams::sf::cmif {
const u32 cmd_id = in_header->command_id;
/* Find a handler. */
decltype(ServiceCommandMeta::handler) cmd_handler = nullptr;
for (size_t i = 0; i < entry_count; i++) {
if (entries[i].Matches(cmd_id, hos_version)) {
cmd_handler = entries[i].GetHandler();
break;
}
}
const auto cmd_handler = FindCommandHandler(entries, entry_count, cmd_id, hos_version);
R_UNLESS(cmd_handler != nullptr, sf::cmif::ResultUnknownCommandId());
/* Invoke handler. */
@@ -73,13 +126,7 @@ namespace ams::sf::cmif {
const u32 cmd_id = in_header->command_id;
/* Find a handler. */
decltype(ServiceCommandMeta::handler) cmd_handler = nullptr;
for (size_t i = 0; i < entry_count; i++) {
if (entries[i].Matches(cmd_id, hos_version)) {
cmd_handler = entries[i].GetHandler();
break;
}
}
const auto cmd_handler = FindCommandHandler(entries, entry_count, cmd_id, hos_version);
/* If we didn't find a handler, forward the request. */
if (cmd_handler == nullptr) {

View File

@@ -41,6 +41,7 @@ namespace ams::socket::impl {
s32 Shutdown(s32 desc, ShutdownMethod how);
s32 Socket(Family domain, Type type, Protocol protocol);
s32 SocketExempt(Family domain, Type type, Protocol protocol);
s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len);

View File

@@ -206,6 +206,8 @@ namespace ams::socket::impl {
/* TODO: socket::resolver::EnableResolverCalls()? Not necessary in our case (htc), but consider calling it. */
g_initialized = true;
return ResultSuccess();
}
@@ -422,6 +424,22 @@ namespace ams::socket::impl {
return result;
}
s32 Socket(Family domain, Type type, Protocol protocol) {
/* Check pre-conditions. */
AMS_ABORT_UNLESS(IsInitialized());
/* Perform the call. */
Errno error = Errno::ESuccess;
int result = ::bsdSocket(static_cast<int>(domain), static_cast<int>(type), static_cast<int>(protocol));
TranslateResultToBsdError(error, result);
if (result < 0) {
socket::impl::SetLastError(error);
}
return result;
}
s32 SocketExempt(Family domain, Type type, Protocol protocol) {
/* Check pre-conditions. */
AMS_ABORT_UNLESS(IsInitialized());

View File

@@ -74,6 +74,10 @@ namespace ams::socket {
return impl::Shutdown(desc, how);
}
s32 Socket(Family domain, Type type, Protocol protocol) {
return impl::Socket(domain, type, protocol);
}
s32 SocketExempt(Family domain, Type type, Protocol protocol) {
return impl::SocketExempt(domain, type, protocol);
}

View File

@@ -729,9 +729,10 @@ namespace ams::spl::impl {
return ResultSuccess();
}
Result TestAesKeySlot(s32 *out_index, s32 keyslot) {
Result TestAesKeySlot(s32 *out_index, bool *out_virtual, s32 keyslot) {
if (g_is_physical_keyslot_allowed && IsPhysicalAesKeySlot(keyslot)) {
*out_index = keyslot;
*out_index = keyslot;
*out_virtual = false;
return ResultSuccess();
}
@@ -740,7 +741,8 @@ namespace ams::spl::impl {
const s32 index = GetVirtualAesKeySlotIndex(keyslot);
R_UNLESS(g_is_aes_keyslot_allocated[index], spl::ResultInvalidKeySlot());
*out_index = index;
*out_index = index;
*out_virtual = true;
return ResultSuccess();
}

View File

@@ -17,10 +17,10 @@
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 1
#define ATMOSPHERE_RELEASE_VERSION_MINOR 2
#define ATMOSPHERE_RELEASE_VERSION_MICRO 2
#define ATMOSPHERE_RELEASE_VERSION_MICRO 6
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 13
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 1
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 2
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 1

View File

@@ -65,8 +65,10 @@
#define ATMOSPHERE_TARGET_FIRMWARE_12_1_0 ATMOSPHERE_TARGET_FIRMWARE(12, 1, 0)
#define ATMOSPHERE_TARGET_FIRMWARE_13_0_0 ATMOSPHERE_TARGET_FIRMWARE(13, 0, 0)
#define ATMOSPHERE_TARGET_FIRMWARE_13_1_0 ATMOSPHERE_TARGET_FIRMWARE(13, 1, 0)
#define ATMOSPHERE_TARGET_FIRMWARE_13_2_0 ATMOSPHERE_TARGET_FIRMWARE(13, 2, 0)
#define ATMOSPHERE_TARGET_FIRMWARE_13_2_1 ATMOSPHERE_TARGET_FIRMWARE(13, 2, 1)
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_13_1_0
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_13_2_1
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0)
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT
@@ -124,6 +126,8 @@ namespace ams {
TargetFirmware_12_1_0 = ATMOSPHERE_TARGET_FIRMWARE_12_1_0,
TargetFirmware_13_0_0 = ATMOSPHERE_TARGET_FIRMWARE_13_0_0,
TargetFirmware_13_1_0 = ATMOSPHERE_TARGET_FIRMWARE_13_1_0,
TargetFirmware_13_2_0 = ATMOSPHERE_TARGET_FIRMWARE_13_2_0,
TargetFirmware_13_2_1 = ATMOSPHERE_TARGET_FIRMWARE_13_2_1,
TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT,

View File

@@ -177,7 +177,11 @@ namespace ams {
static_assert(Value != Base::SuccessValue, "Value != Base::SuccessValue");
public:
constexpr ALWAYS_INLINE operator Result() const { return MakeResult(Value); }
constexpr ALWAYS_INLINE operator ResultSuccess() const { OnResultAbort(Value); }
constexpr ALWAYS_INLINE operator ResultSuccess() const {
OnResultAbort(Value);
__builtin_unreachable();
return ResultSuccess();
}
constexpr ALWAYS_INLINE bool IsSuccess() const { return false; }
constexpr ALWAYS_INLINE bool IsFailure() const { return !this->IsSuccess(); }

View File

@@ -251,7 +251,7 @@ namespace ams::svc {
/* Thread types. */
using ThreadFunc = ams::svc::Address;
#if defined(ATMOSPHERE_ARCH_ARM64)
#if defined(ATMOSPHERE_ARCH_ARM_V8A)
struct ThreadContext {
u64 r[29];
@@ -268,7 +268,7 @@ namespace ams::svc {
};
static_assert(sizeof(ThreadContext) == 0x320);
#elif defined(ATMOSPHERE_ARCH_ARM)
#elif defined(ATMOSPHERE_ARCH_ARM_V7A)
struct ThreadContext {
u32 r[13];
@@ -282,9 +282,14 @@ namespace ams::svc {
u32 fpexc;
u32 tpidr;
};
static_assert(sizeof(ThreadContext) == 0x158);
#else
#if !defined(ATMOSPHERE_IS_EXOSPHERE)
#error "Unknown Architecture for ams::svc::ThreadContext"
#endif
#endif
enum ThreadSuspend : u32 {

View File

@@ -55,6 +55,8 @@
#include <vapours/util/util_atomic.hpp>
#include <vapours/util/util_function_local_static.hpp>
#ifdef ATMOSPHERE_IS_STRATOSPHERE
#include <vapours/util/util_mutex_utils.hpp>
#endif

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/util/util_typed_storage.hpp>
namespace ams::util {
#define AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(_TYPE_, _NAME_, ...) static constinit _TYPE_ _NAME_ { __VA_ARGS__ }
/* NOTE: This must use placement new, to support private constructors. */
#define AMS_FUNCTION_LOCAL_STATIC_IMPL(_LOCKTYPE_, _SCOPELOCKTYPE_, _TYPE_, _NAME_, ...) \
static constinit ::ams::util::TypedStorage<_TYPE_> s_fls_storage_for_##_NAME_ {}; \
static constinit bool s_fls_initialized_##_NAME_ = false; \
static constinit _LOCKTYPE_ s_fls_init_lock_##_NAME_ {}; \
if (AMS_UNLIKELY(!(s_fls_initialized_##_NAME_))) { \
_SCOPELOCKTYPE_ sl_fls_for_##_NAME_ { s_fls_init_lock_##_NAME_ }; \
if (AMS_LIKELY(!(s_fls_initialized_##_NAME_))) { \
new (::ams::util::impl::GetPointerForConstructAt(s_fls_storage_for_##_NAME_)) _TYPE_( __VA_ARGS__ ); \
s_fls_initialized_##_NAME_ = true; \
} \
} \
\
_TYPE_ & _NAME_ = util::GetReference(s_fls_storage_for_##_NAME_)
#if defined(ATMOSPHERE_IS_MESOSPHERE)
#define AMS_FUNCTION_LOCAL_STATIC(_TYPE_, _NAME_, ...) AMS_FUNCTION_LOCAL_STATIC_IMPL(KSpinLock, KScopedSpinLock, _TYPE_, _NAME_, ##__VA_ARGS__)
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
#define AMS_FUNCTION_LOCAL_STATIC(_TYPE_, _NAME_, ...) AMS_FUNCTION_LOCAL_STATIC_IMPL(os::SdkMutex, std::scoped_lock, _TYPE_, _NAME_, ##__VA_ARGS__)
#endif
}

View File

@@ -295,7 +295,7 @@ namespace ams::util {
private:
constexpr explicit ALWAYS_INLINE Iterator(ImplIterator it) : m_impl(it) { /* ... */ }
constexpr explicit ALWAYS_INLINE Iterator(ImplIterator::pointer p) : m_impl(p) { /* ... */ }
constexpr explicit ALWAYS_INLINE Iterator(typename ImplIterator::pointer p) : m_impl(p) { /* ... */ }
constexpr ALWAYS_INLINE ImplIterator GetImplIterator() const {
return m_impl;

View File

@@ -225,7 +225,7 @@ namespace ams::util {
template<typename T, typename Derived>
class OptionalBaseImpl {
protected:
using StoredType = std::remove_const<T>::type;
using StoredType = std::remove_const_t<T>;
template<typename... Args>
constexpr void ConstructImpl(Args &&... args) { static_cast<Derived *>(this)->m_payload.Construct(std::forward<Args>(args)...); }

View File

@@ -201,7 +201,7 @@ namespace ams::util {
constexpr size_type find(basic_string_view str, size_type pos = 0) const noexcept { return this->find(str.m_str, pos, str.m_len); }
__attribute__((nonnull(1)))
__attribute__((nonnull(2)))
constexpr size_type find(const _CharT *str, size_type pos = 0) const noexcept { return this->find(str, pos, traits_type::length(str)); }
constexpr size_type rfind(const _CharT *str, size_type pos, size_type n) const noexcept {
@@ -234,7 +234,7 @@ namespace ams::util {
constexpr size_type rfind(basic_string_view str, size_type pos = 0) const noexcept { return this->rfind(str.m_str, pos, str.m_len); }
__attribute__((nonnull(1)))
__attribute__((nonnull(2)))
constexpr size_type rfind(const _CharT *str, size_type pos = 0) const noexcept { return this->rfind(str, pos, traits_type::length(str)); }
constexpr size_type find_first_of(const _CharT *str, size_type pos, size_t n) const noexcept {
@@ -249,7 +249,7 @@ namespace ams::util {
constexpr size_type find_first_of(basic_string_view str, size_type pos = 0) const noexcept { return this->find_first_of(str.m_str, pos, str.m_len); }
constexpr size_type find_first_of(_CharT c, size_type pos = 0) const noexcept { return this->find(c, pos); }
__attribute__((nonnull(1)))
__attribute__((nonnull(2)))
constexpr size_type find_first_of(const _CharT *str, size_type pos = 0) const noexcept { return this->find_first_of(str, pos, traits_type::length(str)); }
constexpr size_type find_last_of(const _CharT *str, size_type pos, size_t n) const noexcept {
@@ -270,7 +270,7 @@ namespace ams::util {
constexpr size_type find_last_of(basic_string_view str, size_type pos = 0) const noexcept { return this->find_last_of(str.m_str, pos, str.m_len); }
constexpr size_type find_last_of(_CharT c, size_type pos = 0) const noexcept { return this->rfind(c, pos); }
__attribute__((nonnull(1)))
__attribute__((nonnull(2)))
constexpr size_type find_last_of(const _CharT *str, size_type pos = 0) const noexcept { return this->find_first_of(str, pos, traits_type::length(str)); }
constexpr size_type find_first_not_of(const _CharT *str, size_type pos, size_t n) const noexcept {
@@ -293,7 +293,7 @@ namespace ams::util {
constexpr size_type find_first_not_of(basic_string_view str, size_type pos = 0) const noexcept { return this->find_first_not_of(str.m_str, pos, str.m_len); }
__attribute__((nonnull(1)))
__attribute__((nonnull(2)))
constexpr size_type find_first_not_of(const _CharT *str, size_type pos = 0) const noexcept { return this->find_first_not_of(str, pos, traits_type::length(str)); }
constexpr size_type find_last_not_of(const _CharT *str, size_type pos, size_t n) const noexcept {
@@ -328,7 +328,7 @@ namespace ams::util {
constexpr size_type find_last_not_of(basic_string_view str, size_type pos = 0) const noexcept { return this->find_last_not_of(str.m_str, pos, str.m_len); }
__attribute__((nonnull(1)))
__attribute__((nonnull(2)))
constexpr size_type find_last_not_of(const _CharT *str, size_type pos = 0) const noexcept { return this->find_last_not_of(str, pos, traits_type::length(str)); }
constexpr friend bool operator==(const basic_string_view &lhs, const basic_string_view &rhs) noexcept { return lhs.compare(rhs) == 0; }

View File

@@ -45,9 +45,18 @@ namespace ams::util {
return *GetPointer(ts);
}
namespace impl {
template<typename T>
static ALWAYS_INLINE T *GetPointerForConstructAt(TypedStorage<T> &ts) {
return reinterpret_cast<T *>(std::addressof(ts._storage));
}
}
template<typename T, typename... Args>
static ALWAYS_INLINE T *ConstructAt(TypedStorage<T> &ts, Args &&... args) {
return std::construct_at(reinterpret_cast<T *>(std::addressof(ts._storage)), std::forward<Args>(args)...);
return std::construct_at(impl::GetPointerForConstructAt(ts), std::forward<Args>(args)...);
}
template<typename T>

View File

@@ -33,44 +33,6 @@ namespace ams::mitm::fs {
constexpr const char AtmosphereHblWebContentDir[] = "/atmosphere/hbl_html/";
constexpr const char ProgramWebContentDir[] = "/manual_html/";
constinit os::SdkMutex g_data_storage_lock;
constinit os::SdkMutex g_storage_cache_lock;
class StorageCacheEntry : public util::IntrusiveRedBlackTreeBaseNode<StorageCacheEntry> {
public:
using RedBlackKeyType = u64;
private:
ncm::ProgramId m_program_id;
std::weak_ptr<fs::IStorage> m_storage;
public:
StorageCacheEntry(ncm::ProgramId program_id, const std::shared_ptr<fs::IStorage> *sp) : m_program_id(program_id), m_storage(*sp) { /* ... */ }
constexpr ncm::ProgramId GetProgramId() const { return m_program_id; }
constexpr const std::weak_ptr<fs::IStorage> &GetStorage() const { return m_storage; }
void SetStorage(const std::shared_ptr<fs::IStorage> *sp) { m_storage = *sp; }
static constexpr ALWAYS_INLINE int Compare(const RedBlackKeyType &lval, const StorageCacheEntry &rhs) {
const auto rval = rhs.GetProgramId().value;
if (lval < rval) {
return -1;
} else if (lval == rval) {
return 0;
} else {
return 1;
}
}
static constexpr ALWAYS_INLINE int Compare(const StorageCacheEntry &lhs, const StorageCacheEntry &rhs) {
return Compare(lhs.GetProgramId().value, rhs);
}
};
using StorageCache = typename util::IntrusiveRedBlackTreeBaseTraits<StorageCacheEntry>::TreeType<StorageCacheEntry>;
constinit StorageCache g_storage_cache;
constinit os::SdkMutex g_boot0_detect_lock;
constinit bool g_detected_boot0_kind = false;
constinit bool g_is_boot0_custom_public_key = false;
@@ -90,30 +52,6 @@ namespace ams::mitm::fs {
return g_is_boot0_custom_public_key;
}
std::shared_ptr<fs::IStorage> GetStorageCacheEntry(ncm::ProgramId program_id) {
std::scoped_lock lk(g_storage_cache_lock);
if (const auto it = g_storage_cache.find_key(program_id.value); it != g_storage_cache.end()) {
return it->GetStorage().lock();
} else {
return nullptr;
}
}
void SetStorageCacheEntry(ncm::ProgramId program_id, std::shared_ptr<fs::IStorage> *new_intf) {
std::scoped_lock lk(g_storage_cache_lock);
if (auto it = g_storage_cache.find_key(program_id.value); it != g_storage_cache.end()) {
if (auto cur_intf = it->GetStorage().lock(); cur_intf != nullptr) {
*new_intf = cur_intf;
return;
}
}
auto *new_entry = new StorageCacheEntry(program_id, new_intf);
g_storage_cache.insert(*new_entry);
}
bool GetSettingsItemBooleanValue(const char *name, const char *key) {
u8 tmp = 0;
AMS_ABORT_UNLESS(settings::fwdbg::GetSettingsItemValue(std::addressof(tmp), sizeof(tmp), name, key) == sizeof(tmp));
@@ -358,38 +296,8 @@ namespace ams::mitm::fs {
R_TRY(fsOpenDataStorageByCurrentProcessFwd(m_forward_service.get(), std::addressof(data_storage)));
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
/* Get a scoped lock. */
std::scoped_lock lk(g_data_storage_lock);
/* Try to get a storage from the cache. */
{
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(m_client_info.program_id);
if (cached_storage != nullptr) {
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
return ResultSuccess();
}
}
/* Make a new layered romfs, and cache to storage. */
{
std::shared_ptr<fs::IStorage> new_storage = nullptr;
/* Create the layered storage. */
FsFile data_file;
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(data_file), m_client_info.program_id, "romfs.bin", OpenMode_Read))) {
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), m_client_info.program_id);
layered_storage->BeginInitialize();
new_storage = std::move(layered_storage);
} else {
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, m_client_info.program_id);
layered_storage->BeginInitialize();
new_storage = std::move(layered_storage);
}
SetStorageCacheEntry(m_client_info.program_id, std::addressof(new_storage));
out.SetValue(MakeSharedStorage(new_storage), target_object_id);
}
/* Get a layered storage for the process romfs. */
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(m_client_info.program_id, data_storage, true)), target_object_id);
return ResultSuccess();
}
@@ -403,43 +311,13 @@ namespace ams::mitm::fs {
/* Only mitm if there is actually an override romfs. */
R_UNLESS(mitm::fs::HasSdRomfsContent(data_id), sm::mitm::ResultShouldForwardToSession());
/* Try to open the process romfs. */
/* Try to open the data id. */
FsStorage data_storage;
R_TRY(fsOpenDataStorageByDataIdFwd(m_forward_service.get(), std::addressof(data_storage), static_cast<u64>(data_id), static_cast<NcmStorageId>(storage_id)));
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
/* Get a scoped lock. */
std::scoped_lock lk(g_data_storage_lock);
/* Try to get a storage from the cache. */
{
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(data_id);
if (cached_storage != nullptr) {
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
return ResultSuccess();
}
}
/* Make a new layered romfs, and cache to storage. */
{
std::shared_ptr<fs::IStorage> new_storage = nullptr;
/* Create the layered storage. */
FsFile data_file;
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(data_file), data_id, "romfs.bin", OpenMode_Read))) {
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), data_id);
layered_storage->BeginInitialize();
new_storage = std::move(layered_storage);
} else {
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, data_id);
layered_storage->BeginInitialize();
new_storage = std::move(layered_storage);
}
SetStorageCacheEntry(data_id, std::addressof(new_storage));
out.SetValue(MakeSharedStorage(new_storage), target_object_id);
}
/* Get a layered storage for the data id. */
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(data_id, data_storage, false)), target_object_id);
return ResultSuccess();
}
@@ -459,38 +337,8 @@ namespace ams::mitm::fs {
R_TRY(fsOpenDataStorageWithProgramIndexFwd(m_forward_service.get(), std::addressof(data_storage), program_index));
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
/* Get a scoped lock. */
std::scoped_lock lk(g_data_storage_lock);
/* Try to get a storage from the cache. */
{
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(program_id);
if (cached_storage != nullptr) {
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
return ResultSuccess();
}
}
/* Make a new layered romfs, and cache to storage. */
{
std::shared_ptr<fs::IStorage> new_storage = nullptr;
/* Create the layered storage. */
FsFile data_file;
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(data_file), program_id, "romfs.bin", OpenMode_Read))) {
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), program_id);
layered_storage->BeginInitialize();
new_storage = std::move(layered_storage);
} else {
auto layered_storage = std::make_shared<LayeredRomfsStorage>(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, program_id);
layered_storage->BeginInitialize();
new_storage = std::move(layered_storage);
}
SetStorageCacheEntry(program_id, std::addressof(new_storage));
out.SetValue(MakeSharedStorage(new_storage), target_object_id);
}
/* Get a layered storage for the process romfs. */
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(program_id, data_storage, true)), target_object_id);
return ResultSuccess();
}

View File

@@ -28,19 +28,121 @@ namespace ams::mitm::fs {
os::MessageQueue g_req_mq(g_mq_storage + 0, 1);
os::MessageQueue g_ack_mq(g_mq_storage + 1, 1);
class LayeredRomfsStorageHolder : public util::IntrusiveRedBlackTreeBaseNode<LayeredRomfsStorageHolder> {
public:
using RedBlackKeyType = u64;
private:
LayeredRomfsStorageImpl *m_impl;
u32 m_reference_count;
bool m_second_chance;
bool m_process_romfs;
public:
LayeredRomfsStorageHolder(LayeredRomfsStorageImpl *impl, bool process_rom) : m_impl(impl), m_reference_count(1), m_second_chance(true), m_process_romfs(process_rom) {
/* ... */
}
~LayeredRomfsStorageHolder() {
delete m_impl;
}
constexpr LayeredRomfsStorageImpl *GetImpl() const { return m_impl; }
constexpr ncm::ProgramId GetProgramId() const { return m_impl->GetProgramId(); }
constexpr u32 GetReferenceCount() const { return m_reference_count; }
void OpenReferenceImpl() { ++m_reference_count; }
void CloseReferenceImpl() { --m_reference_count; }
bool GetSecondChanceImpl() const { return m_second_chance; }
void SetSecondChanceImpl(bool sc) { m_second_chance = sc; }
bool IsProcessRomfs() const { return m_process_romfs; }
static constexpr ALWAYS_INLINE int Compare(const RedBlackKeyType &lval, const LayeredRomfsStorageHolder &rhs) {
const auto rval = rhs.GetProgramId().value;
if (lval < rval) {
return -1;
} else if (lval == rval) {
return 0;
} else {
return 1;
}
}
static constexpr ALWAYS_INLINE int Compare(const LayeredRomfsStorageHolder &lhs, const LayeredRomfsStorageHolder &rhs) {
return Compare(lhs.GetProgramId().value, rhs);
}
};
using LayeredRomfsStorageSet = typename util::IntrusiveRedBlackTreeBaseTraits<LayeredRomfsStorageHolder>::TreeType<LayeredRomfsStorageHolder>;
constinit os::SdkRecursiveMutex g_storage_set_mutex;
constinit LayeredRomfsStorageSet g_storage_set;
void OpenReference(LayeredRomfsStorageImpl *impl) {
std::scoped_lock lk(g_storage_set_mutex);
auto it = g_storage_set.find_key(impl->GetProgramId().value);
AMS_ABORT_UNLESS(it != g_storage_set.end());
it->OpenReferenceImpl();
}
void CloseReference(LayeredRomfsStorageImpl *impl) {
std::scoped_lock lk(g_storage_set_mutex);
auto it = g_storage_set.find_key(impl->GetProgramId().value);
AMS_ABORT_UNLESS(it != g_storage_set.end());
AMS_ABORT_UNLESS(it->GetReferenceCount() > 0);
it->CloseReferenceImpl();
}
void RomfsInitializerThreadFunction(void *) {
while (true) {
uintptr_t storage_uptr = 0;
g_req_mq.Receive(std::addressof(storage_uptr));
std::shared_ptr<LayeredRomfsStorage> layered_storage = reinterpret_cast<LayeredRomfsStorage *>(storage_uptr)->GetShared();
auto *impl = reinterpret_cast<LayeredRomfsStorageImpl *>(storage_uptr);
g_ack_mq.Send(storage_uptr);
layered_storage->InitializeImpl();
impl->InitializeImpl();
/* Close the initial reference. */
CloseReference(impl);
}
}
void RomfsFinalizerThreadFunction(void *) {
while (true) {
{
std::scoped_lock lk(g_storage_set_mutex);
auto it = g_storage_set.begin();
while (it != g_storage_set.end()) {
if (it->GetReferenceCount() > 0) {
it->SetSecondChanceImpl(true);
++it;
} else if (it->GetSecondChanceImpl()) {
it->SetSecondChanceImpl(false);
++it;
} else {
auto *holder = std::addressof(*it);
it = g_storage_set.erase(it);
delete holder;
}
}
}
os::SleepThread(TimeSpan::FromMilliSeconds(500));
}
}
constexpr size_t RomfsInitializerThreadStackSize = 0x8000;
os::ThreadType g_romfs_initializer_thread;
os::ThreadType g_romfs_finalizer_thread;
alignas(os::ThreadStackAlignment) u8 g_romfs_initializer_thread_stack[RomfsInitializerThreadStackSize];
alignas(os::ThreadStackAlignment) u8 g_romfs_finalizer_thread_stack[os::MemoryPageSize];
void RequestInitializeStorage(uintptr_t storage_uptr) {
std::scoped_lock lk(g_mq_lock);
@@ -49,6 +151,11 @@ namespace ams::mitm::fs {
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_romfs_initializer_thread), RomfsInitializerThreadFunction, nullptr, g_romfs_initializer_thread_stack, sizeof(g_romfs_initializer_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_fs, RomFileSystemInitializeThread)));
os::SetThreadNamePointer(std::addressof(g_romfs_initializer_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm_fs, RomFileSystemInitializeThread));
os::StartThread(std::addressof(g_romfs_initializer_thread));
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_romfs_finalizer_thread), RomfsFinalizerThreadFunction, nullptr, g_romfs_finalizer_thread_stack, sizeof(g_romfs_finalizer_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_fs, RomFileSystemInitializeThread)));
os::SetThreadNamePointer(std::addressof(g_romfs_finalizer_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm_fs, RomFileSystemFinalizeThread));
os::StartThread(std::addressof(g_romfs_finalizer_thread));
g_started_req_thread = true;
}
@@ -58,27 +165,113 @@ namespace ams::mitm::fs {
AMS_ABORT_UNLESS(ack == storage_uptr);
}
class LayeredRomfsStorage : public ams::fs::IStorage {
private:
LayeredRomfsStorageImpl *m_impl;
public:
LayeredRomfsStorage(LayeredRomfsStorageImpl *impl) : m_impl(impl) {
OpenReference(m_impl);
}
virtual ~LayeredRomfsStorage() {
CloseReference(m_impl);
}
virtual Result Read(s64 offset, void *buffer, size_t size) override {
return m_impl->Read(offset, buffer, size);
}
virtual Result GetSize(s64 *out_size) override {
return m_impl->GetSize(out_size);
}
virtual Result Flush() override {
return m_impl->Flush();
}
virtual Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
return m_impl->OperateRange(dst, dst_size, op_id, offset, size, src, src_size);
}
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
/* TODO: Better result code? */
AMS_UNUSED(offset, buffer, size);
return ams::fs::ResultUnsupportedOperation();
}
virtual Result SetSize(s64 size) override {
/* TODO: Better result code? */
AMS_UNUSED(size);
return ams::fs::ResultUnsupportedOperation();
}
};
}
using namespace ams::fs;
LayeredRomfsStorage::LayeredRomfsStorage(std::unique_ptr<IStorage> s_r, std::unique_ptr<IStorage> f_r, ncm::ProgramId pr_id) : m_storage_romfs(std::move(s_r)), m_file_romfs(std::move(f_r)), m_initialize_event(os::EventClearMode_ManualClear), m_program_id(std::move(pr_id)), m_is_initialized(false), m_started_initialize(false) {
std::shared_ptr<ams::fs::IStorage> GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs) {
std::scoped_lock lk(g_storage_set_mutex);
/* Find an existing storage. */
if (auto it = g_storage_set.find_key(program_id.value); it != g_storage_set.end()) {
return std::make_shared<LayeredRomfsStorage>(it->GetImpl());
}
/* We don't have an existing storage. If we're creating process romfs, free any unreferenced process romfs. */
/* This should help prevent too much memory in use at any time. */
if (is_process_romfs) {
auto it = g_storage_set.begin();
while (it != g_storage_set.end()) {
if (it->GetReferenceCount() > 0 || !it->IsProcessRomfs()) {
++it;
} else {
auto *holder = std::addressof(*it);
it = g_storage_set.erase(it);
delete holder;
}
}
}
/* Create a new storage. */
LayeredRomfsStorageImpl *impl;
{
::FsFile data_file;
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(data_file), program_id, "romfs.bin", OpenMode_Read))) {
impl = new LayeredRomfsStorageImpl(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), program_id);
} else {
impl = new LayeredRomfsStorageImpl(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, program_id);
}
}
/* Insert holder. Reference count will now be one. */
g_storage_set.insert(*(new LayeredRomfsStorageHolder(impl, is_process_romfs)));
/* Begin initialization. When this finishes, a decref will occur. */
impl->BeginInitialize();
/* Return a new shared storage for the impl. */
return std::make_shared<LayeredRomfsStorage>(impl);
}
LayeredRomfsStorageImpl::LayeredRomfsStorageImpl(std::unique_ptr<IStorage> s_r, std::unique_ptr<IStorage> f_r, ncm::ProgramId pr_id) : m_storage_romfs(std::move(s_r)), m_file_romfs(std::move(f_r)), m_initialize_event(os::EventClearMode_ManualClear), m_program_id(std::move(pr_id)), m_is_initialized(false), m_started_initialize(false) {
/* ... */
}
LayeredRomfsStorage::~LayeredRomfsStorage() {
LayeredRomfsStorageImpl::~LayeredRomfsStorageImpl() {
for (size_t i = 0; i < m_source_infos.size(); i++) {
m_source_infos[i].Cleanup();
}
}
void LayeredRomfsStorage::BeginInitialize() {
void LayeredRomfsStorageImpl::BeginInitialize() {
AMS_ABORT_UNLESS(!m_started_initialize);
RequestInitializeStorage(reinterpret_cast<uintptr_t>(this));
m_started_initialize = true;
}
void LayeredRomfsStorage::InitializeImpl() {
void LayeredRomfsStorageImpl::InitializeImpl() {
/* Build new virtual romfs. */
romfs::Builder builder(m_program_id);
@@ -98,7 +291,7 @@ namespace ams::mitm::fs {
m_initialize_event.Signal();
}
Result LayeredRomfsStorage::Read(s64 offset, void *buffer, size_t size) {
Result LayeredRomfsStorageImpl::Read(s64 offset, void *buffer, size_t size) {
/* Check if we can succeed immediately. */
R_SUCCEED_IF(size == 0);
@@ -178,7 +371,7 @@ namespace ams::mitm::fs {
return ResultSuccess();
}
Result LayeredRomfsStorage::GetSize(s64 *out_size) {
Result LayeredRomfsStorageImpl::GetSize(s64 *out_size) {
/* Ensure we're initialized. */
if (!m_is_initialized) {
m_initialize_event.Wait();
@@ -188,11 +381,11 @@ namespace ams::mitm::fs {
return ResultSuccess();
}
Result LayeredRomfsStorage::Flush() {
Result LayeredRomfsStorageImpl::Flush() {
return ResultSuccess();
}
Result LayeredRomfsStorage::OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
Result LayeredRomfsStorageImpl::OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
AMS_UNUSED(offset, src, src_size);
switch (op_id) {

View File

@@ -20,7 +20,7 @@
namespace ams::mitm::fs {
class LayeredRomfsStorage : public std::enable_shared_from_this<LayeredRomfsStorage>, public ams::fs::IStorage {
class LayeredRomfsStorageImpl {
private:
std::vector<romfs::SourceInfo> m_source_infos;
std::unique_ptr<ams::fs::IStorage> m_storage_romfs;
@@ -35,32 +35,20 @@ namespace ams::mitm::fs {
return back.virtual_offset + back.size;
}
public:
LayeredRomfsStorage(std::unique_ptr<ams::fs::IStorage> s_r, std::unique_ptr<ams::fs::IStorage> f_r, ncm::ProgramId pr_id);
virtual ~LayeredRomfsStorage();
LayeredRomfsStorageImpl(std::unique_ptr<ams::fs::IStorage> s_r, std::unique_ptr<ams::fs::IStorage> f_r, ncm::ProgramId pr_id);
~LayeredRomfsStorageImpl();
void BeginInitialize();
void InitializeImpl();
std::shared_ptr<LayeredRomfsStorage> GetShared() {
return this->shared_from_this();
}
constexpr ncm::ProgramId GetProgramId() const { return m_program_id; }
virtual Result Read(s64 offset, void *buffer, size_t size) override;
virtual Result GetSize(s64 *out_size) override;
virtual Result Flush() override;
virtual Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
/* TODO: Better result code? */
AMS_UNUSED(offset, buffer, size);
return ams::fs::ResultUnsupportedOperation();
}
virtual Result SetSize(s64 size) override {
/* TODO: Better result code? */
AMS_UNUSED(size);
return ams::fs::ResultUnsupportedOperation();
}
Result Read(s64 offset, void *buffer, size_t size);
Result GetSize(s64 *out_size);
Result Flush();
Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size);
};
std::shared_ptr<ams::fs::IStorage> GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs);
}

View File

@@ -64,28 +64,61 @@ namespace ams::mitm::fs {
};
static_assert(util::is_pod<FileEntry>::value && sizeof(FileEntry) == 0x20);
class DynamicTableCache {
NON_COPYABLE(DynamicTableCache);
NON_MOVEABLE(DynamicTableCache);
private:
static constexpr size_t MaxCachedSize = (1_MB / 4);
private:
size_t m_cache_bitsize;
protected:
void *m_cache;
protected:
DynamicTableCache(size_t sz) {
size_t cache_size = util::CeilingPowerOfTwo(std::min(sz, MaxCachedSize));
m_cache = std::malloc(cache_size);
while (m_cache == nullptr) {
cache_size >>= 1;
AMS_ABORT_UNLESS(cache_size >= 16_KB);
}
m_cache_bitsize = util::CountTrailingZeros(cache_size);
}
~DynamicTableCache() {
std::free(m_cache);
}
ALWAYS_INLINE size_t GetCacheSize() const { return static_cast<size_t>(1) << m_cache_bitsize; }
};
class HashTableStorage : public DynamicTableCache {
public:
HashTableStorage(size_t sz) : DynamicTableCache(sz) { /* ... */ }
ALWAYS_INLINE u32 *GetBuffer() { return reinterpret_cast<u32 *>(m_cache); }
ALWAYS_INLINE size_t GetBufferSize() const { return DynamicTableCache::GetCacheSize(); }
};
template<typename Entry>
class TableReader {
class TableReader : public DynamicTableCache {
NON_COPYABLE(TableReader);
NON_MOVEABLE(TableReader);
private:
static constexpr size_t MaxCachedSize = (1_MB / 4);
static constexpr size_t FallbackCacheSize = 1_KB;
private:
ams::fs::IStorage *m_storage;
size_t m_offset;
size_t m_size;
size_t m_cache_idx;
void *m_cache;
u8 m_fallback_cache[FallbackCacheSize];
private:
ALWAYS_INLINE void Read(size_t ofs, void *dst, size_t size) {
R_ABORT_UNLESS(m_storage->Read(m_offset + ofs, dst, size));
}
ALWAYS_INLINE void ReloadCacheImpl(size_t idx) {
const size_t rel_ofs = idx * MaxCachedSize;
const size_t rel_ofs = idx * this->GetCacheSize();
AMS_ABORT_UNLESS(rel_ofs < m_size);
const size_t new_cache_size = std::min(m_size - rel_ofs, MaxCachedSize);
const size_t new_cache_size = std::min(m_size - rel_ofs, this->GetCacheSize());
this->Read(rel_ofs, m_cache, new_cache_size);
m_cache_idx = idx;
}
@@ -97,23 +130,18 @@ namespace ams::mitm::fs {
}
ALWAYS_INLINE size_t GetCacheIndex(u32 ofs) {
return ofs / MaxCachedSize;
return ofs / this->GetCacheSize();
}
public:
TableReader(ams::fs::IStorage *s, size_t ofs, size_t sz) : m_storage(s), m_offset(ofs), m_size(sz), m_cache_idx(0) {
m_cache = std::malloc(std::min(sz, MaxCachedSize));
TableReader(ams::fs::IStorage *s, size_t ofs, size_t sz) : DynamicTableCache(sz), m_storage(s), m_offset(ofs), m_size(sz), m_cache_idx(0) {
AMS_ABORT_UNLESS(m_cache != nullptr);
this->ReloadCacheImpl(0);
}
~TableReader() {
std::free(m_cache);
}
const Entry *GetEntry(u32 entry_offset) {
this->ReloadCache(this->GetCacheIndex(entry_offset));
const size_t ofs = entry_offset % MaxCachedSize;
const size_t ofs = entry_offset % this->GetCacheSize();
const Entry *entry = reinterpret_cast<const Entry *>(reinterpret_cast<uintptr_t>(m_cache) + ofs);
if (AMS_UNLIKELY(this->GetCacheIndex(entry_offset) != this->GetCacheIndex(entry_offset + sizeof(Entry) + entry->name_size + sizeof(u32)))) {
@@ -125,18 +153,16 @@ namespace ams::mitm::fs {
};
template<typename Entry>
class TableWriter {
class TableWriter : public DynamicTableCache {
NON_COPYABLE(TableWriter);
NON_MOVEABLE(TableWriter);
private:
static constexpr size_t MaxCachedSize = (1_MB / 4);
static constexpr size_t FallbackCacheSize = 1_KB;
private:
::FsFile *m_file;
size_t m_offset;
size_t m_size;
size_t m_cache_idx;
void *m_cache;
u8 m_fallback_cache[FallbackCacheSize];
size_t m_fallback_cache_entry_offset;
size_t m_fallback_cache_entry_size;
@@ -157,8 +183,8 @@ namespace ams::mitm::fs {
AMS_ABORT_UNLESS(!(m_cache_dirty && m_fallback_cache_dirty));
if (m_cache_dirty) {
const size_t ofs = m_cache_idx * MaxCachedSize;
this->Write(ofs, m_cache, std::min(m_size - ofs, MaxCachedSize));
const size_t ofs = m_cache_idx * this->GetCacheSize();
this->Write(ofs, m_cache, std::min(m_size - ofs, this->GetCacheSize()));
m_cache_dirty = false;
}
if (m_fallback_cache_dirty) {
@@ -168,12 +194,12 @@ namespace ams::mitm::fs {
}
ALWAYS_INLINE size_t GetCacheIndex(u32 ofs) {
return ofs / MaxCachedSize;
return ofs / this->GetCacheSize();
}
ALWAYS_INLINE void RefreshCacheImpl() {
const size_t cur_cache = m_cache_idx * MaxCachedSize;
this->Read(cur_cache, m_cache, std::min(m_size - cur_cache, MaxCachedSize));
const size_t cur_cache = m_cache_idx * this->GetCacheSize();
this->Read(cur_cache, m_cache, std::min(m_size - cur_cache, this->GetCacheSize()));
}
ALWAYS_INLINE void RefreshCache(u32 entry_offset) {
@@ -184,29 +210,27 @@ namespace ams::mitm::fs {
}
}
public:
TableWriter(::FsFile *f, size_t ofs, size_t sz) : m_file(f), m_offset(ofs), m_size(sz), m_cache_idx(0), m_fallback_cache_entry_offset(), m_fallback_cache_entry_size(), m_cache_dirty(), m_fallback_cache_dirty() {
const size_t cache_size = std::min(sz, MaxCachedSize);
m_cache = std::malloc(cache_size);
TableWriter(::FsFile *f, size_t ofs, size_t sz) : DynamicTableCache(sz), m_file(f), m_offset(ofs), m_size(sz), m_cache_idx(0), m_fallback_cache_entry_offset(), m_fallback_cache_entry_size(), m_cache_dirty(), m_fallback_cache_dirty() {
AMS_ABORT_UNLESS(m_cache != nullptr);
std::memset(m_cache, 0, cache_size);
std::memset(m_cache, 0, this->GetCacheSize());
std::memset(m_fallback_cache, 0, sizeof(m_fallback_cache));
for (size_t cur = 0; cur < m_size; cur += MaxCachedSize) {
this->Write(cur, m_cache, std::min(m_size - cur, MaxCachedSize));
for (size_t cur = 0; cur < m_size; cur += this->GetCacheSize()) {
this->Write(cur, m_cache, std::min(m_size - cur, this->GetCacheSize()));
}
}
~TableWriter() {
this->Flush();
std::free(m_cache);
}
Entry *GetEntry(u32 entry_offset, u32 name_len) {
this->RefreshCache(entry_offset);
const size_t ofs = entry_offset % MaxCachedSize;
const size_t ofs = entry_offset % this->GetCacheSize();
Entry *entry = reinterpret_cast<Entry *>(reinterpret_cast<uintptr_t>(m_cache) + ofs);
if (ofs + sizeof(Entry) + util::AlignUp(name_len, sizeof(u32)) > MaxCachedSize) {
if (ofs + sizeof(Entry) + util::AlignUp(name_len, sizeof(u32)) > this->GetCacheSize()) {
this->Flush();
m_fallback_cache_entry_offset = entry_offset;
@@ -226,7 +250,6 @@ namespace ams::mitm::fs {
using DirectoryTableWriter = TableWriter<DirectoryEntry>;
using FileTableWriter = TableWriter<FileEntry>;
constexpr inline u32 CalculatePathHash(u32 parent, const char *_path, u32 start, size_t path_len) {
const unsigned char *path = reinterpret_cast<const unsigned char *>(_path);
u32 hash = parent ^ 123456789;
@@ -526,113 +549,151 @@ namespace ams::mitm::fs {
}
}
/* Populate file tables. */
{
/* Allocate the hash table. */
void *fht_buf = std::malloc(m_file_hash_table_size);
AMS_ABORT_UNLESS(fht_buf != nullptr);
u32 *file_hash_table = reinterpret_cast<u32 *>(fht_buf);
std::memset(file_hash_table, 0xFF, m_file_hash_table_size);
ON_SCOPE_EXIT {
R_ABORT_UNLESS(fsFileWrite(std::addressof(metadata_file), m_dir_hash_table_size + m_dir_table_size, file_hash_table, m_file_hash_table_size, FsWriteOption_None));
std::free(fht_buf);
};
/* Set all files' hash value = hash index. */
for (const auto &it : m_files) {
BuildFileContext *cur_file = it.get();
cur_file->hash_value = CalculatePathHash(cur_file->parent->entry_offset, cur_file->path.get(), 0, cur_file->path_len) % num_file_hash_table_entries;
}
/* Write the file table. */
{
FileTableWriter file_table(std::addressof(metadata_file), m_dir_hash_table_size + m_dir_table_size + m_file_hash_table_size, m_file_table_size);
/* Set all directories' hash value = hash index. */
for (const auto &it : m_directories) {
BuildDirectoryContext *cur_dir = it.get();
cur_dir->hash_value = CalculatePathHash(cur_dir == m_root ? 0 : cur_dir->parent->entry_offset, cur_dir->path.get(), 0, cur_dir->path_len) % num_dir_hash_table_entries;
}
/* Write hash tables. */
{
HashTableStorage hash_table_storage(std::max(m_dir_hash_table_size, m_file_hash_table_size));
u32 *hash_table = hash_table_storage.GetBuffer();
size_t hash_table_size = hash_table_storage.GetBufferSize();
/* Write the file hash table. */
for (size_t ofs = 0; ofs < m_file_hash_table_size; ofs += hash_table_size) {
std::memset(hash_table, 0xFF, hash_table_size);
const u32 ofs_ind = ofs / sizeof(u32);
const u32 end_ind = (ofs + hash_table_size) / sizeof(u32);
for (const auto &it : m_files) {
BuildFileContext *cur_file = it.get();
FileEntry *cur_entry = file_table.GetEntry(cur_file->entry_offset, cur_file->path_len);
if (cur_file->HasHashMark()) {
continue;
}
/* Set entry fields. */
cur_entry->parent = cur_file->parent->entry_offset;
cur_entry->sibling = (cur_file->sibling == nullptr) ? EmptyEntry : cur_file->sibling->entry_offset;
cur_entry->offset = cur_file->offset;
cur_entry->size = cur_file->size;
if (const auto hash_ind = cur_file->hash_value; ofs_ind <= hash_ind && hash_ind < end_ind) {
cur_file->hash_value = hash_table[hash_ind - ofs_ind];
hash_table[hash_ind - ofs_ind] = cur_file->entry_offset;
/* Insert into hash table. */
const u32 name_size = cur_file->path_len;
const size_t hash_ind = CalculatePathHash(cur_entry->parent, cur_file->path.get(), 0, name_size) % num_file_hash_table_entries;
cur_entry->hash = file_hash_table[hash_ind];
file_hash_table[hash_ind] = cur_file->entry_offset;
cur_file->SetHashMark();
}
}
/* Set name. */
cur_entry->name_size = name_size;
if (name_size) {
std::memcpy(cur_entry->name, cur_file->path.get(), name_size);
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
cur_entry->name[i] = 0;
R_ABORT_UNLESS(fsFileWrite(std::addressof(metadata_file), m_dir_hash_table_size + m_dir_table_size + ofs, hash_table, std::min(m_file_hash_table_size - ofs, hash_table_size), FsWriteOption_None));
}
/* Write the directory hash table. */
for (size_t ofs = 0; ofs < m_dir_hash_table_size; ofs += hash_table_size) {
std::memset(hash_table, 0xFF, hash_table_size);
const u32 ofs_ind = ofs / sizeof(u32);
const u32 end_ind = (ofs + hash_table_size) / sizeof(u32);
for (const auto &it : m_directories) {
BuildDirectoryContext *cur_dir = it.get();
if (cur_dir->HasHashMark()) {
continue;
}
if (const auto hash_ind = cur_dir->hash_value; ofs_ind <= hash_ind && hash_ind < end_ind) {
cur_dir->hash_value = hash_table[hash_ind - ofs_ind];
hash_table[hash_ind - ofs_ind] = cur_dir->entry_offset;
cur_dir->SetHashMark();
}
}
R_ABORT_UNLESS(fsFileWrite(std::addressof(metadata_file), ofs, hash_table, std::min(m_dir_hash_table_size - ofs, hash_table_size), FsWriteOption_None));
}
}
/* Write the file table. */
{
FileTableWriter file_table(std::addressof(metadata_file), m_dir_hash_table_size + m_dir_table_size + m_file_hash_table_size, m_file_table_size);
for (const auto &it : m_files) {
BuildFileContext *cur_file = it.get();
FileEntry *cur_entry = file_table.GetEntry(cur_file->entry_offset, cur_file->path_len);
cur_file->ClearHashMark();
/* Set entry fields. */
cur_entry->parent = cur_file->parent->entry_offset;
cur_entry->sibling = (cur_file->sibling == nullptr) ? EmptyEntry : cur_file->sibling->entry_offset;
cur_entry->offset = cur_file->offset;
cur_entry->size = cur_file->size;
cur_entry->hash = cur_file->hash_value;
/* Set name. */
const u32 name_size = cur_file->path_len;
cur_entry->name_size = name_size;
if (name_size) {
std::memcpy(cur_entry->name, cur_file->path.get(), name_size);
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
cur_entry->name[i] = 0;
}
}
/* Emplace a source. */
switch (cur_file->source_type) {
case DataSourceType::Storage:
case DataSourceType::File:
{
/* Try to compact if possible. */
auto &back = out_infos->back();
if (back.source_type == cur_file->source_type) {
back.size = cur_file->offset + FilePartitionOffset + cur_file->size - back.virtual_offset;
} else {
out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, cur_file->orig_offset + FilePartitionOffset);
}
}
}
/* Emplace a source. */
switch (cur_file->source_type) {
case DataSourceType::Storage:
case DataSourceType::File:
{
/* Try to compact if possible. */
auto &back = out_infos->back();
if (back.source_type == cur_file->source_type) {
back.size = cur_file->offset + FilePartitionOffset + cur_file->size - back.virtual_offset;
} else {
out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, cur_file->orig_offset + FilePartitionOffset);
}
}
break;
case DataSourceType::LooseSdFile:
{
char *new_path = new char[cur_file->GetPathLength() + 1];
cur_file->GetPath(new_path);
out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, new_path);
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
break;
case DataSourceType::LooseSdFile:
{
char *new_path = new char[cur_file->GetPathLength() + 1];
cur_file->GetPath(new_path);
out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, new_path);
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}
/* Populate directory tables. */
/* Write the directory table. */
{
/* Allocate the hash table. */
void *dht_buf = std::malloc(m_dir_hash_table_size);
AMS_ABORT_UNLESS(dht_buf != nullptr);
u32 *dir_hash_table = reinterpret_cast<u32 *>(dht_buf);
std::memset(dir_hash_table, 0xFF, m_dir_hash_table_size);
ON_SCOPE_EXIT {
R_ABORT_UNLESS(fsFileWrite(std::addressof(metadata_file), 0, dir_hash_table, m_dir_hash_table_size, FsWriteOption_None));
std::free(dht_buf);
};
DirectoryTableWriter dir_table(std::addressof(metadata_file), m_dir_hash_table_size, m_dir_table_size);
/* Write the file table. */
{
DirectoryTableWriter dir_table(std::addressof(metadata_file), m_dir_hash_table_size, m_dir_table_size);
for (const auto &it : m_directories) {
BuildDirectoryContext *cur_dir = it.get();
DirectoryEntry *cur_entry = dir_table.GetEntry(cur_dir->entry_offset, cur_dir->path_len);
for (const auto &it : m_directories) {
BuildDirectoryContext *cur_dir = it.get();
DirectoryEntry *cur_entry = dir_table.GetEntry(cur_dir->entry_offset, cur_dir->path_len);
cur_dir->ClearHashMark();
/* Set entry fields. */
cur_entry->parent = cur_dir == m_root ? 0 : cur_dir->parent->entry_offset;
cur_entry->sibling = (cur_dir->sibling == nullptr) ? EmptyEntry : cur_dir->sibling->entry_offset;
cur_entry->child = (cur_dir->child == nullptr) ? EmptyEntry : cur_dir->child->entry_offset;
cur_entry->file = (cur_dir->file == nullptr) ? EmptyEntry : cur_dir->file->entry_offset;
/* Set entry fields. */
cur_entry->parent = cur_dir == m_root ? 0 : cur_dir->parent->entry_offset;
cur_entry->sibling = (cur_dir->sibling == nullptr) ? EmptyEntry : cur_dir->sibling->entry_offset;
cur_entry->child = (cur_dir->child == nullptr) ? EmptyEntry : cur_dir->child->entry_offset;
cur_entry->file = (cur_dir->file == nullptr) ? EmptyEntry : cur_dir->file->entry_offset;
cur_entry->hash = cur_dir->hash_value;
/* Insert into hash table. */
const u32 name_size = cur_dir->path_len;
const size_t hash_ind = CalculatePathHash(cur_entry->parent, cur_dir->path.get(), 0, name_size) % num_dir_hash_table_entries;
cur_entry->hash = dir_hash_table[hash_ind];
dir_hash_table[hash_ind] = cur_dir->entry_offset;
/* Set name. */
cur_entry->name_size = name_size;
if (name_size) {
std::memcpy(cur_entry->name, cur_dir->path.get(), name_size);
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
cur_entry->name[i] = 0;
}
/* Set name. */
const u32 name_size = cur_dir->path_len;
cur_entry->name_size = name_size;
if (name_size) {
std::memcpy(cur_entry->name, cur_dir->path.get(), name_size);
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
cur_entry->name[i] = 0;
}
}
}

View File

@@ -16,10 +16,11 @@
#pragma once
#include <stratosphere.hpp>
#include "../dns_mitm/dnsmitm_debug.hpp"
namespace ams::mitm::fs::romfs {
enum class DataSourceType {
enum class DataSourceType : u8 {
Storage,
File,
LooseSdFile,
@@ -120,10 +121,11 @@ namespace ams::mitm::fs::romfs {
BuildFileContext *file;
u32 path_len;
u32 entry_offset;
u32 hash_value;
struct RootTag{};
BuildDirectoryContext(RootTag) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), path_len(0), entry_offset(0) {
BuildDirectoryContext(RootTag) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), path_len(0), entry_offset(0), hash_value(0xFFFFFFFF) {
this->path = std::make_unique<char[]>(1);
}
@@ -154,6 +156,18 @@ namespace ams::mitm::fs::romfs {
dst[parent_len + 1 + this->path_len] = '\x00';
return parent_len + 1 + this->path_len;
}
bool HasHashMark() const {
return reinterpret_cast<uintptr_t>(this->sibling) & UINT64_C(0x8000000000000000);
}
void SetHashMark() {
this->sibling = reinterpret_cast<BuildDirectoryContext *>(reinterpret_cast<uintptr_t>(this->sibling) | UINT64_C(0x8000000000000000));
}
void ClearHashMark() {
this->sibling = reinterpret_cast<BuildDirectoryContext *>(reinterpret_cast<uintptr_t>(this->sibling) & ~UINT64_C(0x8000000000000000));
}
};
struct BuildFileContext {
@@ -168,9 +182,10 @@ namespace ams::mitm::fs::romfs {
s64 orig_offset;
u32 path_len;
u32 entry_offset;
u32 hash_value;
DataSourceType source_type;
BuildFileContext(const char *entry_name, size_t entry_name_len, s64 sz, s64 o_o, DataSourceType type) : parent(nullptr), sibling(nullptr), offset(0), size(sz), orig_offset(o_o), entry_offset(0), source_type(type) {
BuildFileContext(const char *entry_name, size_t entry_name_len, s64 sz, s64 o_o, DataSourceType type) : parent(nullptr), sibling(nullptr), offset(0), size(sz), orig_offset(o_o), entry_offset(0), hash_value(0xFFFFFFFF), source_type(type) {
this->path_len = entry_name_len;
this->path = std::unique_ptr<char[]>(new char[this->path_len + 1]);
std::memcpy(this->path.get(), entry_name, entry_name_len);
@@ -197,6 +212,18 @@ namespace ams::mitm::fs::romfs {
dst[parent_len + 1 + this->path_len] = '\x00';
return parent_len + 1 + this->path_len;
}
bool HasHashMark() const {
return reinterpret_cast<uintptr_t>(this->sibling) & UINT64_C(0x8000000000000000);
}
void SetHashMark() {
this->sibling = reinterpret_cast<BuildFileContext *>(reinterpret_cast<uintptr_t>(this->sibling) | UINT64_C(0x8000000000000000));
}
void ClearHashMark() {
this->sibling = reinterpret_cast<BuildFileContext *>(reinterpret_cast<uintptr_t>(this->sibling) & ~UINT64_C(0x8000000000000000));
}
};
class DirectoryTableReader;

View File

@@ -378,6 +378,12 @@ namespace ams::settings::fwdbg {
/* 0 = Disabled, 1 = Enabled */
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_htc", "u8!0x0"));
/* Controls whether atmosphere's dmnt.gen2 gdbstub should run as a standalone via sockets. */
/* Note that this setting is ignored (and treated as 0) when htc is enabled. */
/* Note that this setting may disappear in the future. */
/* 0 = Disabled, 1 = Enabled */
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_standalone_gdbstub", "u8!0x0"));
/* Controls whether atmosphere's log manager is enabled. */
/* Note that this setting is ignored (and treated as 1) when htc is enabled. */
/* 0 = Disabled, 1 = Enabled */

View File

@@ -152,7 +152,7 @@ namespace ams::mitm::sysupdater {
ON_SCOPE_EXIT { std::free(data_buffer); };
/* Declare helper for result validation. */
auto ValidateResult = [&] ALWAYS_INLINE_LAMBDA (Result result) -> Result {
auto ValidateResult = [&](Result result) ALWAYS_INLINE_LAMBDA -> Result {
*out_result = result;
return result;
};

View File

@@ -101,11 +101,9 @@ namespace ams::creport {
m_has_extra_info = has_extra_info;
if (this->OpenProcess(process_id)) {
ON_SCOPE_EXIT { this->Close(); };
/* Parse info from the crashed process. */
this->ProcessExceptions();
m_module_list->FindModulesFromThreadInfo(m_debug_handle, m_crashed_thread);
m_module_list->FindModulesFromThreadInfo(m_debug_handle, m_crashed_thread, this->Is64Bit());
m_thread_list->ReadFromProcess(m_debug_handle, m_thread_tls_map, this->Is64Bit());
/* Associate module list to threads. */
@@ -120,7 +118,7 @@ namespace ams::creport {
/* Nintendo's creport finds extra modules by looking at all threads if application, */
/* but there's no reason for us not to always go looking. */
for (size_t i = 0; i < m_thread_list->GetThreadCount(); i++) {
m_module_list->FindModulesFromThreadInfo(m_debug_handle, m_thread_list->GetThreadInfo(i));
m_module_list->FindModulesFromThreadInfo(m_debug_handle, m_thread_list->GetThreadInfo(i), this->Is64Bit());
}
/* Cache the module base address to send to fatal. */
@@ -324,6 +322,11 @@ namespace ams::creport {
}
}
/* If we're open, we need to close here. */
if (this->IsOpen()) {
this->Close();
}
/* Finalize our heap. */
std::destroy_at(m_module_list);
std::destroy_at(m_thread_list);
@@ -358,7 +361,7 @@ namespace ams::creport {
}
void CrashReport::SaveToFile(ScopedFile &file) {
file.WriteFormat("Atmosphère Crash Report (v1.6):\n");
file.WriteFormat("Atmosphère Crash Report (v1.7):\n");
file.WriteFormat("Result: 0x%X (2%03d-%04d)\n\n", m_result.GetValue(), m_result.GetModule(), m_result.GetDescription());

View File

@@ -103,13 +103,17 @@ namespace ams {
g_crash_report.Initialize();
/* Try to debug the crashed process. */
g_crash_report.BuildReport(crashed_pid, has_extra_info);
if (!g_crash_report.IsComplete()) {
return;
}
{
g_crash_report.BuildReport(crashed_pid, has_extra_info);
ON_SCOPE_EXIT { if (g_crash_report.IsOpen()) { g_crash_report.Close(); } };
/* Save report to file. */
g_crash_report.SaveReport(enable_screenshot);
if (!g_crash_report.IsComplete()) {
return;
}
/* Save report to file. */
g_crash_report.SaveReport(enable_screenshot);
}
/* Try to terminate the process, if we should. */
const auto fw_ver = hos::GetVersion();

View File

@@ -27,7 +27,7 @@ namespace ams::creport {
struct ModulePath {
u32 zero;
u32 path_length;
s32 path_length;
char path[ModulePathLengthMax];
};
static_assert(sizeof(ModulePath) == 0x208, "ModulePath definition!");
@@ -58,26 +58,26 @@ namespace ams::creport {
}
}
void ModuleList::FindModulesFromThreadInfo(os::NativeHandle debug_handle, const ThreadInfo &thread) {
void ModuleList::FindModulesFromThreadInfo(os::NativeHandle debug_handle, const ThreadInfo &thread, bool is_64_bit) {
/* Set the debug handle, for access in other member functions. */
m_debug_handle = debug_handle;
/* Try to add the thread's PC. */
this->TryAddModule(thread.GetPC());
this->TryAddModule(thread.GetPC(), is_64_bit);
/* Try to add the thread's LR. */
this->TryAddModule(thread.GetLR());
this->TryAddModule(thread.GetLR(), is_64_bit);
/* Try to add all the addresses in the thread's stacktrace. */
for (size_t i = 0; i < thread.GetStackTraceSize(); i++) {
this->TryAddModule(thread.GetStackTrace(i));
this->TryAddModule(thread.GetStackTrace(i), is_64_bit);
}
}
void ModuleList::TryAddModule(uintptr_t guess) {
void ModuleList::TryAddModule(uintptr_t guess, bool is_64_bit) {
/* Try to locate module from guess. */
uintptr_t base_address = 0;
if (!this->TryFindModule(std::addressof(base_address), guess)) {
if (!this->TryFindModule(std::addressof(base_address), guess, is_64_bit)) {
return;
}
@@ -105,9 +105,18 @@ namespace ams::creport {
module.end_address = mi.base_address + mi.size;
GetModuleName(module.name, module.start_address, module.end_address);
GetModuleId(module.module_id, module.end_address);
/* Some homebrew won't have a name. Add a fake one for readability. */
/* Default to no symbol table. */
module.has_sym_table = false;
if (std::strcmp(module.name, "") == 0) {
/* Some homebrew won't have a name. Add a fake one for readability. */
util::SNPrintf(module.name, sizeof(module.name), "[%02x%02x%02x%02x]", module.module_id[0], module.module_id[1], module.module_id[2], module.module_id[3]);
} else {
/* The module has a name, and so might have a symbol table. Try to add it, if it does. */
if (is_64_bit) {
DetectModuleSymbolTable(module);
}
}
}
@@ -125,7 +134,9 @@ namespace ams::creport {
}
}
bool ModuleList::TryFindModule(uintptr_t *out_address, uintptr_t guess) {
bool ModuleList::TryFindModule(uintptr_t *out_address, uintptr_t guess, bool is_64_bit) {
AMS_UNUSED(is_64_bit);
/* Query the memory region our guess falls in. */
svc::MemoryInfo mi;
svc::PageInfo pi;
@@ -199,7 +210,7 @@ namespace ams::creport {
}
/* Also validate that we're looking at a valid name. */
if (rodata_start.module_path.zero != 0 || rodata_start.module_path.path_length != strnlen(rodata_start.module_path.path, sizeof(rodata_start.module_path.path))) {
if (rodata_start.module_path.zero != 0 || rodata_start.module_path.path_length <= 0) {
return;
}
}
@@ -208,7 +219,7 @@ namespace ams::creport {
/* Start after last slash in path. */
const char *path = rodata_start.module_path.path;
int ofs;
for (ofs = rodata_start.module_path.path_length; ofs >= 0; ofs--) {
for (ofs = std::min<size_t>(rodata_start.module_path.path_length, sizeof(rodata_start.module_path.path)); ofs >= 0; ofs--) {
if (path[ofs] == '/' || path[ofs] == '\\') {
break;
}
@@ -216,8 +227,8 @@ namespace ams::creport {
ofs++;
/* Copy name to output. */
const size_t name_size = std::min(ModuleNameLengthMax, sizeof(rodata_start.module_path.path) - ofs);
std::strncpy(out_name, path + ofs, name_size);
const size_t name_size = std::min(ModuleNameLengthMax, std::min<size_t>(rodata_start.module_path.path_length, sizeof(rodata_start.module_path.path)) - ofs);
std::memcpy(out_name, path + ofs, name_size);
out_name[ModuleNameLengthMax - 1] = '\x00';
}
@@ -247,6 +258,133 @@ namespace ams::creport {
}
}
void ModuleList::DetectModuleSymbolTable(ModuleInfo &module) {
/* If we already have a symbol table, no more parsing is needed. */
if (module.has_sym_table) {
return;
}
/* Declare temporaries. */
u64 temp_64;
u32 temp_32;
/* Get module state. */
svc::MemoryInfo mi;
svc::PageInfo pi;
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), m_debug_handle, module.start_address))) {
return;
}
const auto module_state = mi.state;
/* Verify .rodata is read-only with same state as .text. */
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), m_debug_handle, module.end_address)) || mi.permission != svc::MemoryPermission_Read || mi.state != module_state) {
return;
}
/* Read the first instruction of .text. */
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_32)), m_debug_handle, module.start_address, sizeof(temp_32)))) {
return;
}
/* We want to find the symbol table/.dynamic. */
uintptr_t dyn_address = 0;
uintptr_t sym_tab = 0;
uintptr_t str_tab = 0;
size_t num_sym = 0;
/* Detect module type. */
if (temp_32 == 0) {
/* Module is dynamically loaded by rtld. */
u32 mod_offset;
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(mod_offset)), m_debug_handle, module.start_address + sizeof(u32), sizeof(u32)))) {
return;
}
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_32)), m_debug_handle, module.start_address + mod_offset, sizeof(u32)))) {
return;
}
if (temp_32 != rocrt::ModuleHeaderVersion) { /* MOD0 */
return;
}
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_32)), m_debug_handle, module.start_address + mod_offset + sizeof(u32), sizeof(u32)))) {
return;
}
dyn_address = module.start_address + mod_offset + temp_32;
} else if (temp_32 == 0x14000002) {
/* Module embeds rtld. */
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_32)), m_debug_handle, module.start_address + 0x5C, sizeof(u32)))) {
return;
}
if (temp_32 != 0x94000002) {
return;
}
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_32)), m_debug_handle, module.start_address + 0x60, sizeof(u32)))) {
return;
}
dyn_address = module.start_address + 0x60 + temp_32;
} else {
/* Module has unknown format. */
return;
}
/* Locate tables inside .dyn. */
for (size_t ofs = 0; /* ... */; ofs += 0x10) {
/* Read the DynamicTag. */
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_64)), m_debug_handle, dyn_address + ofs, sizeof(u64)))) {
return;
}
if (temp_64 == 0) {
/* We're done parsing .dyn. */
break;
} else if (temp_64 == 4) {
/* We found DT_HASH */
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_64)), m_debug_handle, dyn_address + ofs + sizeof(u64), sizeof(u64)))) {
return;
}
/* Read nchain, to get the number of symbols. */
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_32)), m_debug_handle, module.start_address + temp_64 + sizeof(u32), sizeof(u32)))) {
return;
}
num_sym = temp_32;
} else if (temp_64 == 5) {
/* We found DT_STRTAB */
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_64)), m_debug_handle, dyn_address + ofs + sizeof(u64), sizeof(u64)))) {
return;
}
str_tab = module.start_address + temp_64;
} else if (temp_64 == 6) {
/* We found DT_SYMTAB */
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_64)), m_debug_handle, dyn_address + ofs + sizeof(u64), sizeof(u64)))) {
return;
}
sym_tab = module.start_address + temp_64;
}
}
/* Check that we found all the tables. */
if (!(sym_tab != 0 && str_tab != 0 && num_sym != 0)) {
return;
}
module.has_sym_table = true;
module.sym_tab = sym_tab;
module.str_tab = str_tab;
module.num_sym = static_cast<u32>(num_sym);
}
const char *ModuleList::GetFormattedAddressString(uintptr_t address) {
/* Print default formatted string. */
util::SNPrintf(m_address_str_buf, sizeof(m_address_str_buf), "%016lx", address);
@@ -255,8 +393,52 @@ namespace ams::creport {
for (size_t i = 0; i < m_num_modules; i++) {
const auto& module = m_modules[i];
if (module.start_address <= address && address < module.end_address) {
if (module.has_sym_table) {
/* Try to locate an appropriate symbol. */
for (size_t j = 0; j < module.num_sym; ++j) {
/* Read symbol from the module's symbol table. */
struct {
u32 st_name;
u8 st_info;
u8 st_other;
u16 st_shndx;
u64 st_value;
u64 st_size;
} sym;
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(sym)), m_debug_handle, module.sym_tab + j * sizeof(sym), sizeof(sym)))) {
break;
}
/* Check the symbol is valid/STT_FUNC. */
if (sym.st_shndx == 0 || ((sym.st_shndx & 0xFF00) == 0xFF00)) {
continue;
}
if ((sym.st_info & 0xF) != 2) {
continue;
}
/* Check the address. */
const uintptr_t func_start = module.start_address + sym.st_value;
if (func_start <= address && address < func_start + sym.st_size) {
/* Read the symbol name. */
const uintptr_t sym_address = module.str_tab + sym.st_name;
char sym_name[0x80];
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(sym_name), m_debug_handle, sym_address, sizeof(sym_name)))) {
break;
}
/* Ensure null-termination. */
sym_name[sizeof(sym_name) - 1] = '\x00';
/* Print the symbol. */
util::SNPrintf(m_address_str_buf, sizeof(m_address_str_buf), "%016lx (%s + 0x%lx) (%s + 0x%lx)", address, module.name, address - module.start_address, sym_name, address - func_start);
return m_address_str_buf;
}
}
}
util::SNPrintf(m_address_str_buf, sizeof(m_address_str_buf), "%016lx (%s + 0x%lx)", address, module.name, address - module.start_address);
break;
return m_address_str_buf;
}
}

View File

@@ -30,6 +30,10 @@ namespace ams::creport {
u8 module_id[ModuleIdSize];
u64 start_address;
u64 end_address;
bool has_sym_table;
u64 sym_tab;
u64 str_tab;
u32 num_sym;
};
private:
os::NativeHandle m_debug_handle;
@@ -37,7 +41,7 @@ namespace ams::creport {
ModuleInfo m_modules[ModuleCountMax];
/* For pretty-printing. */
char m_address_str_buf[0x280];
char m_address_str_buf[1_KB];
public:
ModuleList() : m_debug_handle(os::InvalidNativeHandle), m_num_modules(0) {
std::memset(m_modules, 0, sizeof(m_modules));
@@ -51,14 +55,15 @@ namespace ams::creport {
return m_modules[i].start_address;
}
void FindModulesFromThreadInfo(os::NativeHandle debug_handle, const ThreadInfo &thread);
void FindModulesFromThreadInfo(os::NativeHandle debug_handle, const ThreadInfo &thread, bool is_64_bit);
const char *GetFormattedAddressString(uintptr_t address);
void SaveToFile(ScopedFile &file);
private:
bool TryFindModule(uintptr_t *out_address, uintptr_t guess);
void TryAddModule(uintptr_t guess);
bool TryFindModule(uintptr_t *out_address, uintptr_t guess, bool is_64_bit);
void TryAddModule(uintptr_t guess, bool is_64_bit);
void GetModuleName(char *out_name, uintptr_t text_start, uintptr_t ro_start);
void GetModuleId(u8 *out, uintptr_t ro_start);
void DetectModuleSymbolTable(ModuleInfo &module);
};
}

Some files were not shown because too many files have changed in this diff Show More