Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1019bc54e6 | ||
|
|
96631d8225 | ||
|
|
d32dd0f04a | ||
|
|
ddfc16731f | ||
|
|
26714c7f3b | ||
|
|
767e702a70 | ||
|
|
9d5e652fbd | ||
|
|
1d39e06f32 | ||
|
|
21b7884653 | ||
|
|
14ad2f0ba0 | ||
|
|
fcc7ce49d9 | ||
|
|
f98c7cba98 | ||
|
|
496adb0018 | ||
|
|
09074798cd | ||
|
|
e256261b80 | ||
|
|
47218f0da8 | ||
|
|
409a48ec73 | ||
|
|
0c93cefd39 | ||
|
|
0bbc907907 | ||
|
|
4a4a1f0e87 | ||
|
|
705dd95375 | ||
|
|
2d5751356b | ||
|
|
b140834b7e | ||
|
|
e3d2af6b3f | ||
|
|
c17ad1e0e3 | ||
|
|
6145b3b72c | ||
|
|
aba7e4ca7d | ||
|
|
4cc5e9cdfd | ||
|
|
ca0308c7ca | ||
|
|
1d908295fe | ||
|
|
277bd101c5 | ||
|
|
3dce23773a | ||
|
|
4489513f7c | ||
|
|
5eabca7f04 | ||
|
|
a73e0e8f16 | ||
|
|
2161365f4f | ||
|
|
258a83684e | ||
|
|
d2a757c39e | ||
|
|
6cf5205a28 | ||
|
|
b9c90b9234 | ||
|
|
94e18b8c93 | ||
|
|
4e92687cab | ||
|
|
2a0b99d9f9 | ||
|
|
d1f3c4904b | ||
|
|
92321ccbc8 | ||
|
|
db3004e844 | ||
|
|
3e97e4addf | ||
|
|
4b7b33809f | ||
|
|
e81a1ce5a8 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -91,7 +91,11 @@ dkms.conf
|
|||||||
**/out
|
**/out
|
||||||
**/build
|
**/build
|
||||||
**/build_nintendo_nx_arm64
|
**/build_nintendo_nx_arm64
|
||||||
|
**/build_nintendo_nx_arm64_armv8a
|
||||||
**/build_nintendo_nx_arm
|
**/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_x64
|
||||||
**/build_nintendo_nx_x86
|
**/build_nintendo_nx_x86
|
||||||
|
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -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/0100000000000042
|
||||||
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000420
|
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/010000000000B240
|
||||||
|
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D609
|
||||||
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D623
|
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/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
|
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/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/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/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
|
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
|
@build_romfs atmosphere-$(AMSVER)/stratosphere_romfs atmosphere-$(AMSVER)/atmosphere/stratosphere.romfs
|
||||||
rm -r atmosphere-$(AMSVER)/stratosphere_romfs
|
rm -r atmosphere-$(AMSVER)/stratosphere_romfs
|
||||||
|
|||||||
@@ -1,4 +1,67 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
## 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.
|
||||||
|
+ Unfortunately, there were several problems, and if your console began trying to use the new services atmosphere would show a fatal error with code 0xCAF6 (sprofile::ResultInvalidState()).
|
||||||
|
+ With actual test data in hand, a test program was written and it was verified that our implementation can successfully import/access profile data now.
|
||||||
|
+ Hopefully there are no more issues, and I sincerely apologize for anyone who got an 0xCAF6 fatal due to this.
|
||||||
|
+ A number of minor improvements were made to `mesosphère`, including:
|
||||||
|
+ KThread::GetContextForSchedulerLoop was implemented in assembly (using static assertions to verify offset-of-context-in-struct is correct).
|
||||||
|
+ This saves an unnecessary function call in the middle of the scheduler hot loop, replacing it with an addition instruction, which should improve microperformance.
|
||||||
|
+ Mesosphere's hardware maintenance instructions were audited via a script and now directly match Nintendo's kernels.
|
||||||
|
+ Notably, this inserts a missing instruction synchronization barrier when validating that slab heaps may be constructed.
|
||||||
|
+ This missing ISB could cause an abort on certain (see: particularly sensitive) hardware on boot if the relevant codepath was speculatively executed (it normally only executes on game launch...)
|
||||||
|
+ The SVC handlers for performing light IPC (normally unused) from 32-bit process were fixed in Mesosphere.
|
||||||
|
+ A bug was fixed that would cause the register x27 to be overwritten with the contents of x26 when returning from a user exception handler.
|
||||||
|
+ A bug was fixed that would cause the kernel to use the userland stack pointer instead of the kernel stack pointer while generating an error report for a kernel abort.
|
||||||
|
+ General system stability improvements to enhance the user's experience.
|
||||||
## 1.2.1
|
## 1.2.1
|
||||||
+ Support was implemented for 13.1.0.
|
+ Support was implemented for 13.1.0.
|
||||||
+ `mesosphère` was updated to reflect the kernel behavioral changes made in 13.1.0.
|
+ `mesosphère` was updated to reflect the kernel behavioral changes made in 13.1.0.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
[subrepo]
|
[subrepo]
|
||||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||||
branch = master
|
branch = master
|
||||||
commit = 13c6987cc43d0b9a874e87e3ce8eaffd1c72fa69
|
commit = c4d0335b79da7207b49abf1988f45b0168b692f0
|
||||||
parent = cb38b0b929f332a52ceeee5c27cb0fd83ff03433
|
parent = 96631d8225611b2b490ec1e8b5a84c1b0e53157a
|
||||||
method = merge
|
method = merge
|
||||||
cmdver = 0.4.1
|
cmdver = 0.4.1
|
||||||
|
|||||||
11
libraries/config/arch/armv4t/arch.mk
Normal file
11
libraries/config/arch/armv4t/arch.mk
Normal 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 +=
|
||||||
11
libraries/config/arch/armv8a/arch.mk
Normal file
11
libraries/config/arch/armv8a/arch.mk
Normal 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 +=
|
||||||
@@ -39,6 +39,9 @@ export ATMOSPHERE_ARCH_NAME := arm64
|
|||||||
export ATMOSPHERE_BOARD_NAME := nintendo_nx
|
export ATMOSPHERE_BOARD_NAME := nintendo_nx
|
||||||
export ATMOSPHERE_OS_NAME := horizon
|
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
|
export ATMOSPHERE_CPU_EXTENSIONS := arm_crypto_extension aarch64_crypto_extension
|
||||||
else ifeq ($(ATMOSPHERE_CPU),arm7tdmi)
|
else ifeq ($(ATMOSPHERE_CPU),arm7tdmi)
|
||||||
export ATMOSPHERE_ARCH_DIR := arm
|
export ATMOSPHERE_ARCH_DIR := arm
|
||||||
@@ -49,6 +52,9 @@ export ATMOSPHERE_ARCH_NAME := arm
|
|||||||
export ATMOSPHERE_BOARD_NAME := nintendo_nx
|
export ATMOSPHERE_BOARD_NAME := nintendo_nx
|
||||||
export ATMOSPHERE_OS_NAME := horizon
|
export ATMOSPHERE_OS_NAME := horizon
|
||||||
|
|
||||||
|
export ATMOSPHERE_SUB_ARCH_DIR = armv4t
|
||||||
|
export ATMOSPHERE_SUB_ARCH_NAME = armv4t
|
||||||
|
|
||||||
export ATMOSPHERE_CPU_EXTENSIONS :=
|
export ATMOSPHERE_CPU_EXTENSIONS :=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -64,6 +70,9 @@ export ATMOSPHERE_ARCH_NAME := arm64
|
|||||||
export ATMOSPHERE_BOARD_NAME := qemu_virt
|
export ATMOSPHERE_BOARD_NAME := qemu_virt
|
||||||
export ATMOSPHERE_OS_NAME := horizon
|
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
|
export ATMOSPHERE_CPU_EXTENSIONS := arm_crypto_extension aarch64_crypto_extension
|
||||||
endif
|
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_OS_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/os/$(ATMOSPHERE_OS_DIR)
|
||||||
export ATMOSPHERE_CPU_MAKE_DIR := $(ATMOSPHERE_ARCH_MAKE_DIR)/cpu/$(ATMOSPHERE_CPU_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_LIBRARY_DIR := lib_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)
|
||||||
export ATMOSPHERE_BUILD_DIR := build_$(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_ARCH_MAKE_DIR)/arch.mk
|
||||||
include $(ATMOSPHERE_BOARD_MAKE_DIR)/board.mk
|
include $(ATMOSPHERE_BOARD_MAKE_DIR)/board.mk
|
||||||
include $(ATMOSPHERE_OS_MAKE_DIR)/os.mk
|
include $(ATMOSPHERE_OS_MAKE_DIR)/os.mk
|
||||||
include $(ATMOSPHERE_CPU_MAKE_DIR)/cpu.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
|
# get atmosphere git revision information
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
@@ -125,12 +147,20 @@ DATA := data
|
|||||||
INCLUDES := include
|
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,))
|
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),))
|
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,))
|
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), \
|
ALL_SOURCE_DIRS=$(foreach d,$(call GENERAL_SOURCE_DIRS,$1), \
|
||||||
$d \
|
$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,board,$(ATMOSPHERE_BOARD_DIR)) \
|
||||||
$(call SPECIFIC_SOURCE_DIRS,$d,os,$(ATMOSPHERE_OS_DIR)) \
|
$(call SPECIFIC_SOURCE_DIRS,$d,os,$(ATMOSPHERE_OS_DIR)) \
|
||||||
$(call SPECIFIC_SOURCE_DIRS,$d,cpu,$(ATMOSPHERE_ARCH_DIR)/$(ATMOSPHERE_CPU_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)/*.os.*.$2)) \
|
||||||
$(notdir $(wildcard $(dir)/.cpu.*.$2)), \
|
$(notdir $(wildcard $(dir)/.cpu.*.$2)), \
|
||||||
$(notdir $(wildcard $(dir)/*.$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),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,$(dir),os,$(ATMOSPHERE_OS_NAME),$2)) \
|
||||||
$(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES_EX,$(dir),cpu,$(ATMOSPHERE_CPU_NAME) $(ATMOSPHERE_CPU_EXTENSIONS),$2))
|
$(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES_EX,$(dir),cpu,$(ATMOSPHERE_CPU_NAME) $(ATMOSPHERE_CPU_EXTENSIONS),$2))
|
||||||
|
|||||||
@@ -93,3 +93,4 @@
|
|||||||
|
|
||||||
/* Deferred includes. */
|
/* Deferred includes. */
|
||||||
#include <mesosphere/kern_k_auto_object_impls.hpp>
|
#include <mesosphere/kern_k_auto_object_impls.hpp>
|
||||||
|
#include <mesosphere/kern_k_scheduler_impls.hpp>
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ namespace ams::kern::arch::arm64::init {
|
|||||||
|
|
||||||
/* Invalidate the entire tlb. */
|
/* Invalidate the entire tlb. */
|
||||||
cpu::DataSynchronizationBarrierInnerShareable();
|
cpu::DataSynchronizationBarrierInnerShareable();
|
||||||
cpu::InvalidateEntireTlb();
|
cpu::InvalidateEntireTlbInnerShareable();
|
||||||
|
|
||||||
/* Copy data, if we should. */
|
/* Copy data, if we should. */
|
||||||
const u64 negative_block_size_for_mask = static_cast<u64>(-static_cast<s64>(block_size));
|
const u64 negative_block_size_for_mask = static_cast<u64>(-static_cast<s64>(block_size));
|
||||||
@@ -350,7 +350,6 @@ namespace ams::kern::arch::arm64::init {
|
|||||||
/* If we don't already have an L2 table, we need to make a new one. */
|
/* If we don't already have an L2 table, we need to make a new one. */
|
||||||
if (!l1_entry->IsTable()) {
|
if (!l1_entry->IsTable()) {
|
||||||
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
|
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
|
||||||
ClearNewPageTable(new_table);
|
|
||||||
*l1_entry = L1PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
|
*l1_entry = L1PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
|
||||||
cpu::DataSynchronizationBarrierInnerShareable();
|
cpu::DataSynchronizationBarrierInnerShareable();
|
||||||
}
|
}
|
||||||
@@ -361,12 +360,12 @@ namespace ams::kern::arch::arm64::init {
|
|||||||
if (util::IsAligned(GetInteger(virt_addr), L2ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L2ContiguousBlockSize) && size >= L2ContiguousBlockSize) {
|
if (util::IsAligned(GetInteger(virt_addr), L2ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L2ContiguousBlockSize) && size >= L2ContiguousBlockSize) {
|
||||||
for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) {
|
for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) {
|
||||||
l2_entry[i] = L2PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, PageTableEntry::SoftwareReservedBit_None, true);
|
l2_entry[i] = L2PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, PageTableEntry::SoftwareReservedBit_None, true);
|
||||||
cpu::DataSynchronizationBarrierInnerShareable();
|
|
||||||
|
|
||||||
virt_addr += L2BlockSize;
|
virt_addr += L2BlockSize;
|
||||||
phys_addr += L2BlockSize;
|
phys_addr += L2BlockSize;
|
||||||
size -= L2BlockSize;
|
size -= L2BlockSize;
|
||||||
}
|
}
|
||||||
|
cpu::DataSynchronizationBarrierInnerShareable();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,7 +383,6 @@ namespace ams::kern::arch::arm64::init {
|
|||||||
/* If we don't already have an L3 table, we need to make a new one. */
|
/* If we don't already have an L3 table, we need to make a new one. */
|
||||||
if (!l2_entry->IsTable()) {
|
if (!l2_entry->IsTable()) {
|
||||||
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
|
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
|
||||||
ClearNewPageTable(new_table);
|
|
||||||
*l2_entry = L2PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
|
*l2_entry = L2PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
|
||||||
cpu::DataSynchronizationBarrierInnerShareable();
|
cpu::DataSynchronizationBarrierInnerShareable();
|
||||||
}
|
}
|
||||||
@@ -395,12 +393,12 @@ namespace ams::kern::arch::arm64::init {
|
|||||||
if (util::IsAligned(GetInteger(virt_addr), L3ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L3ContiguousBlockSize) && size >= L3ContiguousBlockSize) {
|
if (util::IsAligned(GetInteger(virt_addr), L3ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L3ContiguousBlockSize) && size >= L3ContiguousBlockSize) {
|
||||||
for (size_t i = 0; i < L3ContiguousBlockSize / L3BlockSize; i++) {
|
for (size_t i = 0; i < L3ContiguousBlockSize / L3BlockSize; i++) {
|
||||||
l3_entry[i] = L3PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, PageTableEntry::SoftwareReservedBit_None, true);
|
l3_entry[i] = L3PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, PageTableEntry::SoftwareReservedBit_None, true);
|
||||||
cpu::DataSynchronizationBarrierInnerShareable();
|
|
||||||
|
|
||||||
virt_addr += L3BlockSize;
|
virt_addr += L3BlockSize;
|
||||||
phys_addr += L3BlockSize;
|
phys_addr += L3BlockSize;
|
||||||
size -= L3BlockSize;
|
size -= L3BlockSize;
|
||||||
}
|
}
|
||||||
|
cpu::DataSynchronizationBarrierInnerShareable();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,9 @@
|
|||||||
/* TODO: Different header for this? */
|
/* TODO: Different header for this? */
|
||||||
#define AMS_KERN_NUM_SUPERVISOR_CALLS 0xC0
|
#define AMS_KERN_NUM_SUPERVISOR_CALLS 0xC0
|
||||||
|
|
||||||
|
/* ams::kern::KThread, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp */
|
||||||
|
#define THREAD_THREAD_CONTEXT 0xD0
|
||||||
|
|
||||||
/* ams::kern::KThread::StackParameters, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp */
|
/* ams::kern::KThread::StackParameters, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp */
|
||||||
#define THREAD_STACK_PARAMETERS_SIZE 0x30
|
#define THREAD_STACK_PARAMETERS_SIZE 0x30
|
||||||
#define THREAD_STACK_PARAMETERS_SVC_PERMISSION 0x00
|
#define THREAD_STACK_PARAMETERS_SVC_PERMISSION 0x00
|
||||||
|
|||||||
@@ -60,6 +60,11 @@ namespace ams::kern::arch::arm64::cpu {
|
|||||||
__asm__ __volatile__("isb" ::: "memory");
|
__asm__ __volatile__("isb" ::: "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE void EnsureInstructionConsistencyInnerShareable() {
|
||||||
|
DataSynchronizationBarrierInnerShareable();
|
||||||
|
InstructionMemoryBarrier();
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE void EnsureInstructionConsistency() {
|
ALWAYS_INLINE void EnsureInstructionConsistency() {
|
||||||
DataSynchronizationBarrier();
|
DataSynchronizationBarrier();
|
||||||
InstructionMemoryBarrier();
|
InstructionMemoryBarrier();
|
||||||
@@ -177,7 +182,6 @@ namespace ams::kern::arch::arm64::cpu {
|
|||||||
NOINLINE void SynchronizeAllCores();
|
NOINLINE void SynchronizeAllCores();
|
||||||
|
|
||||||
/* Cache management helpers. */
|
/* Cache management helpers. */
|
||||||
void ClearPageToZeroImpl(void *);
|
|
||||||
void StoreEntireCacheForInit();
|
void StoreEntireCacheForInit();
|
||||||
void FlushEntireCacheForInit();
|
void FlushEntireCacheForInit();
|
||||||
|
|
||||||
@@ -190,10 +194,16 @@ namespace ams::kern::arch::arm64::cpu {
|
|||||||
|
|
||||||
void InvalidateEntireInstructionCache();
|
void InvalidateEntireInstructionCache();
|
||||||
|
|
||||||
ALWAYS_INLINE void ClearPageToZero(void *page) {
|
ALWAYS_INLINE void ClearPageToZero(void * const page) {
|
||||||
MESOSPHERE_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(page), PageSize));
|
MESOSPHERE_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(page), PageSize));
|
||||||
MESOSPHERE_ASSERT(page != nullptr);
|
MESOSPHERE_ASSERT(page != nullptr);
|
||||||
ClearPageToZeroImpl(page);
|
|
||||||
|
uintptr_t cur = reinterpret_cast<uintptr_t>(__builtin_assume_aligned(page, PageSize));
|
||||||
|
const uintptr_t last = cur + PageSize - DataCacheLineSize;
|
||||||
|
|
||||||
|
for (/* ... */; cur <= last; cur += DataCacheLineSize) {
|
||||||
|
__asm__ __volatile__("dc zva, %[cur]" :: [cur]"r"(cur) : "memory");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE void InvalidateTlbByAsid(u32 asid) {
|
ALWAYS_INLINE void InvalidateTlbByAsid(u32 asid) {
|
||||||
@@ -213,6 +223,11 @@ namespace ams::kern::arch::arm64::cpu {
|
|||||||
EnsureInstructionConsistency();
|
EnsureInstructionConsistency();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE void InvalidateEntireTlbInnerShareable() {
|
||||||
|
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
|
||||||
|
EnsureInstructionConsistencyInnerShareable();
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE void InvalidateEntireTlbDataOnly() {
|
ALWAYS_INLINE void InvalidateEntireTlbDataOnly() {
|
||||||
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
|
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
|
||||||
DataSynchronizationBarrier();
|
DataSynchronizationBarrier();
|
||||||
|
|||||||
@@ -105,35 +105,48 @@ namespace ams::kern::arch::arm64 {
|
|||||||
Result UnbindLocal(s32 irq);
|
Result UnbindLocal(s32 irq);
|
||||||
Result ClearGlobal(s32 irq);
|
Result ClearGlobal(s32 irq);
|
||||||
Result ClearLocal(s32 irq);
|
Result ClearLocal(s32 irq);
|
||||||
public:
|
private:
|
||||||
static ALWAYS_INLINE u32 DisableInterrupts() {
|
[[nodiscard]] static ALWAYS_INLINE u32 GetInterruptsEnabledState() {
|
||||||
u64 intr_state;
|
u64 intr_state;
|
||||||
__asm__ __volatile__("mrs %[intr_state], daif\n"
|
__asm__ __volatile__("mrs %[intr_state], daif\n"
|
||||||
"msr daifset, #2"
|
"ubfx %[intr_state], %[intr_state], #7, #1"
|
||||||
: [intr_state]"=r"(intr_state)
|
: [intr_state]"=r"(intr_state)
|
||||||
:: "memory");
|
:: "memory");
|
||||||
return intr_state;
|
return intr_state;
|
||||||
}
|
}
|
||||||
|
public:
|
||||||
|
static ALWAYS_INLINE void EnableInterrupts() {
|
||||||
|
__asm__ __volatile__("msr daifclr, #2" ::: "memory");
|
||||||
|
}
|
||||||
|
|
||||||
static ALWAYS_INLINE u32 EnableInterrupts() {
|
static ALWAYS_INLINE void DisableInterrupts() {
|
||||||
u64 intr_state;
|
__asm__ __volatile__("msr daifset, #2" ::: "memory");
|
||||||
__asm__ __volatile__("mrs %[intr_state], daif\n"
|
}
|
||||||
"msr daifclr, #2"
|
|
||||||
: [intr_state]"=r"(intr_state)
|
[[nodiscard]] static ALWAYS_INLINE u32 GetInterruptsEnabledStateAndDisableInterrupts() {
|
||||||
:: "memory");
|
const auto intr_state = GetInterruptsEnabledState();
|
||||||
|
DisableInterrupts();
|
||||||
|
return intr_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static ALWAYS_INLINE u32 GetInterruptsEnabledStateAndEnableInterrupts() {
|
||||||
|
const auto intr_state = GetInterruptsEnabledState();
|
||||||
|
EnableInterrupts();
|
||||||
return intr_state;
|
return intr_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ALWAYS_INLINE void RestoreInterrupts(u32 intr_state) {
|
static ALWAYS_INLINE void RestoreInterrupts(u32 intr_state) {
|
||||||
u64 cur_state;
|
u64 tmp;
|
||||||
__asm__ __volatile__("mrs %[cur_state], daif" : [cur_state]"=r"(cur_state));
|
__asm__ __volatile__("mrs %[tmp], daif\n"
|
||||||
__asm__ __volatile__("msr daif, %[intr_state]" :: [intr_state]"r"((cur_state & ~0x80ul) | (intr_state & 0x80)));
|
"bfi %[tmp], %[intr_state], #7, #1\n"
|
||||||
|
"msr daif, %[tmp]"
|
||||||
|
: [tmp]"=&r"(tmp)
|
||||||
|
: [intr_state]"r"(intr_state)
|
||||||
|
: "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
static ALWAYS_INLINE bool AreInterruptsEnabled() {
|
static ALWAYS_INLINE bool AreInterruptsEnabled() {
|
||||||
u64 intr_state;
|
return GetInterruptsEnabledState() == 0;
|
||||||
__asm__ __volatile__("mrs %[intr_state], daif" : [intr_state]"=r"(intr_state));
|
|
||||||
return (intr_state & 0x80) == 0;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -219,27 +219,27 @@ namespace ams::kern::arch::arm64 {
|
|||||||
|
|
||||||
Result ChangePermissions(KProcessAddress virt_addr, size_t num_pages, PageTableEntry entry_template, DisableMergeAttribute disable_merge_attr, bool refresh_mapping, PageLinkedList *page_list, bool reuse_ll);
|
Result ChangePermissions(KProcessAddress virt_addr, size_t num_pages, PageTableEntry entry_template, DisableMergeAttribute disable_merge_attr, bool refresh_mapping, PageLinkedList *page_list, bool reuse_ll);
|
||||||
|
|
||||||
static void PteDataSynchronizationBarrier() {
|
static ALWAYS_INLINE void PteDataSynchronizationBarrier() {
|
||||||
cpu::DataSynchronizationBarrierInnerShareable();
|
cpu::DataSynchronizationBarrierInnerShareable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ClearPageTable(KVirtualAddress table) {
|
static ALWAYS_INLINE void ClearPageTable(KVirtualAddress table) {
|
||||||
cpu::ClearPageToZero(GetVoidPointer(table));
|
cpu::ClearPageToZero(GetVoidPointer(table));
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnTableUpdated() const {
|
ALWAYS_INLINE void OnTableUpdated() const {
|
||||||
cpu::InvalidateTlbByAsid(m_asid);
|
cpu::InvalidateTlbByAsid(m_asid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnKernelTableUpdated() const {
|
ALWAYS_INLINE void OnKernelTableUpdated() const {
|
||||||
cpu::InvalidateEntireTlbDataOnly();
|
cpu::InvalidateEntireTlbDataOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnKernelTableSinglePageUpdated(KProcessAddress virt_addr) const {
|
ALWAYS_INLINE void OnKernelTableSinglePageUpdated(KProcessAddress virt_addr) const {
|
||||||
cpu::InvalidateTlbByVaDataOnly(virt_addr);
|
cpu::InvalidateTlbByVaDataOnly(virt_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoteUpdated() const {
|
ALWAYS_INLINE void NoteUpdated() const {
|
||||||
cpu::DataSynchronizationBarrier();
|
cpu::DataSynchronizationBarrier();
|
||||||
|
|
||||||
if (this->IsKernel()) {
|
if (this->IsKernel()) {
|
||||||
@@ -249,7 +249,7 @@ namespace ams::kern::arch::arm64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoteSingleKernelPageUpdated(KProcessAddress virt_addr) const {
|
ALWAYS_INLINE void NoteSingleKernelPageUpdated(KProcessAddress virt_addr) const {
|
||||||
MESOSPHERE_ASSERT(this->IsKernel());
|
MESOSPHERE_ASSERT(this->IsKernel());
|
||||||
|
|
||||||
cpu::DataSynchronizationBarrier();
|
cpu::DataSynchronizationBarrier();
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ namespace ams::kern::arch::arm64 {
|
|||||||
|
|
||||||
/* Select L1 cache. */
|
/* Select L1 cache. */
|
||||||
cpu::SetCsselrEl1(0);
|
cpu::SetCsselrEl1(0);
|
||||||
|
cpu::InstructionMemoryBarrier();
|
||||||
|
|
||||||
/* Check that the L1 cache is not direct-mapped. */
|
/* Check that the L1 cache is not direct-mapped. */
|
||||||
return cpu::CacheSizeIdRegisterAccessor().GetAssociativity() != 0;
|
return cpu::CacheSizeIdRegisterAccessor().GetAssociativity() != 0;
|
||||||
|
|||||||
@@ -40,6 +40,12 @@
|
|||||||
/* of the right side, and so this does not actually cost any space. */
|
/* of the right side, and so this does not actually cost any space. */
|
||||||
#define MESOSPHERE_ENABLE_DEVIRTUALIZED_DYNAMIC_CAST
|
#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 */
|
/* NOTE: This uses currently-reserved bits inside the MapRange capability */
|
||||||
/* in order to support large physical addresses (40-bit instead of 36). */
|
/* 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. */
|
/* This is toggleable in order to disable it if N ever uses those bits. */
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace ams::kern {
|
|||||||
return m_slab_heap->Allocate(m_page_allocator);
|
return m_slab_heap->Allocate(m_page_allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Free(T *t) const {
|
ALWAYS_INLINE void Free(T *t) const {
|
||||||
m_slab_heap->Free(t);
|
m_slab_heap->Free(t);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -62,8 +62,7 @@ namespace ams::kern {
|
|||||||
KThread *m_idle_thread;
|
KThread *m_idle_thread;
|
||||||
util::Atomic<KThread *> m_current_thread;
|
util::Atomic<KThread *> m_current_thread;
|
||||||
public:
|
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.needs_scheduling = true;
|
||||||
m_state.interrupt_task_runnable = false;
|
m_state.interrupt_task_runnable = false;
|
||||||
m_state.should_count_idle = false;
|
m_state.should_count_idle = false;
|
||||||
@@ -211,18 +210,6 @@ namespace ams::kern {
|
|||||||
static consteval bool ValidateAssemblyOffsets();
|
static consteval bool ValidateAssemblyOffsets();
|
||||||
};
|
};
|
||||||
|
|
||||||
consteval bool KScheduler::ValidateAssemblyOffsets() {
|
|
||||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.needs_scheduling) == KSCHEDULER_NEEDS_SCHEDULING);
|
|
||||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.interrupt_task_runnable) == KSCHEDULER_INTERRUPT_TASK_RUNNABLE);
|
|
||||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.highest_priority_thread) == KSCHEDULER_HIGHEST_PRIORITY_THREAD);
|
|
||||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.idle_thread_stack) == KSCHEDULER_IDLE_THREAD_STACK);
|
|
||||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.prev_thread) == KSCHEDULER_PREVIOUS_THREAD);
|
|
||||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.interrupt_task_manager) == KSCHEDULER_INTERRUPT_TASK_MANAGER);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
static_assert(KScheduler::ValidateAssemblyOffsets());
|
|
||||||
|
|
||||||
class KScopedSchedulerLock : KScopedLock<KScheduler::LockType> {
|
class KScopedSchedulerLock : KScopedLock<KScheduler::LockType> {
|
||||||
public:
|
public:
|
||||||
explicit ALWAYS_INLINE KScopedSchedulerLock() : KScopedLock(KScheduler::s_scheduler_lock) { /* ... */ }
|
explicit ALWAYS_INLINE KScopedSchedulerLock() : KScopedLock(KScheduler::s_scheduler_lock) { /* ... */ }
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <mesosphere/kern_common.hpp>
|
||||||
|
#include <mesosphere/kern_k_scheduler.hpp>
|
||||||
|
#include <mesosphere/kern_select_interrupt_manager.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
/* NOTE: This header is included after all main headers. */
|
||||||
|
consteval bool KScheduler::ValidateAssemblyOffsets() {
|
||||||
|
static_assert(AMS_OFFSETOF(KScheduler, m_state.needs_scheduling) == KSCHEDULER_NEEDS_SCHEDULING);
|
||||||
|
static_assert(AMS_OFFSETOF(KScheduler, m_state.interrupt_task_runnable) == KSCHEDULER_INTERRUPT_TASK_RUNNABLE);
|
||||||
|
static_assert(AMS_OFFSETOF(KScheduler, m_state.highest_priority_thread) == KSCHEDULER_HIGHEST_PRIORITY_THREAD);
|
||||||
|
static_assert(AMS_OFFSETOF(KScheduler, m_state.idle_thread_stack) == KSCHEDULER_IDLE_THREAD_STACK);
|
||||||
|
static_assert(AMS_OFFSETOF(KScheduler, m_state.prev_thread) == KSCHEDULER_PREVIOUS_THREAD);
|
||||||
|
static_assert(AMS_OFFSETOF(KScheduler, m_state.interrupt_task_manager) == KSCHEDULER_INTERRUPT_TASK_MANAGER);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
static_assert(KScheduler::ValidateAssemblyOffsets());
|
||||||
|
|
||||||
|
ALWAYS_INLINE void KScheduler::RescheduleOtherCores(u64 cores_needing_scheduling) {
|
||||||
|
if (const u64 core_mask = cores_needing_scheduling & ~(1ul << m_core_id); core_mask != 0) {
|
||||||
|
cpu::DataSynchronizationBarrier();
|
||||||
|
Kernel::GetInterruptManager().SendInterProcessorInterrupt(KInterruptName_Scheduler, core_mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -405,8 +405,6 @@ namespace ams::kern {
|
|||||||
constexpr ThreadState GetState() const { return static_cast<ThreadState>(m_thread_state & ThreadState_Mask); }
|
constexpr ThreadState GetState() const { return static_cast<ThreadState>(m_thread_state & ThreadState_Mask); }
|
||||||
constexpr ThreadState GetRawState() const { return m_thread_state; }
|
constexpr ThreadState GetRawState() const { return m_thread_state; }
|
||||||
|
|
||||||
NOINLINE KThreadContext *GetContextForSchedulerLoop();
|
|
||||||
|
|
||||||
constexpr uintptr_t GetConditionVariableKey() const { return m_condvar_key; }
|
constexpr uintptr_t GetConditionVariableKey() const { return m_condvar_key; }
|
||||||
constexpr uintptr_t GetAddressArbiterKey() const { return m_condvar_key; }
|
constexpr uintptr_t GetAddressArbiterKey() const { return m_condvar_key; }
|
||||||
|
|
||||||
@@ -624,9 +622,7 @@ namespace ams::kern {
|
|||||||
void OnTimer();
|
void OnTimer();
|
||||||
void DoWorkerTaskImpl();
|
void DoWorkerTaskImpl();
|
||||||
public:
|
public:
|
||||||
static constexpr bool IsConditionVariableThreadTreeValid() {
|
static consteval bool IsKThreadStructurallyValid();
|
||||||
return ConditionVariableThreadTreeTraits::IsValid();
|
|
||||||
}
|
|
||||||
|
|
||||||
static KThread *GetThreadFromId(u64 thread_id);
|
static KThread *GetThreadFromId(u64 thread_id);
|
||||||
static Result GetThreadList(s32 *out_num_threads, ams::kern::svc::KUserPointer<u64 *> out_thread_ids, s32 max_out_count);
|
static Result GetThreadList(s32 *out_num_threads, ams::kern::svc::KUserPointer<u64 *> out_thread_ids, s32 max_out_count);
|
||||||
@@ -634,7 +630,18 @@ namespace ams::kern {
|
|||||||
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
|
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
|
||||||
};
|
};
|
||||||
static_assert(alignof(KThread) == 0x10);
|
static_assert(alignof(KThread) == 0x10);
|
||||||
static_assert(KThread::IsConditionVariableThreadTreeValid());
|
|
||||||
|
consteval bool KThread::IsKThreadStructurallyValid() {
|
||||||
|
/* Check that the condition variable tree is valid. */
|
||||||
|
static_assert(ConditionVariableThreadTreeTraits::IsValid());
|
||||||
|
|
||||||
|
/* Check that the assembly offsets are valid. */
|
||||||
|
static_assert(AMS_OFFSETOF(KThread, m_thread_context) == THREAD_THREAD_CONTEXT);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(KThread::IsKThreadStructurallyValid());
|
||||||
|
|
||||||
class KScopedDisableDispatch {
|
class KScopedDisableDispatch {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace ams::kern {
|
|||||||
private:
|
private:
|
||||||
u32 m_prev_intr_state;
|
u32 m_prev_intr_state;
|
||||||
public:
|
public:
|
||||||
ALWAYS_INLINE KScopedInterruptDisable() : m_prev_intr_state(KInterruptManager::DisableInterrupts()) { /* ... */ }
|
ALWAYS_INLINE KScopedInterruptDisable() : m_prev_intr_state(KInterruptManager::GetInterruptsEnabledStateAndDisableInterrupts()) { /* ... */ }
|
||||||
ALWAYS_INLINE ~KScopedInterruptDisable() { KInterruptManager::RestoreInterrupts(m_prev_intr_state); }
|
ALWAYS_INLINE ~KScopedInterruptDisable() { KInterruptManager::RestoreInterrupts(m_prev_intr_state); }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ namespace ams::kern {
|
|||||||
private:
|
private:
|
||||||
u32 m_prev_intr_state;
|
u32 m_prev_intr_state;
|
||||||
public:
|
public:
|
||||||
ALWAYS_INLINE KScopedInterruptEnable() : m_prev_intr_state(KInterruptManager::EnableInterrupts()) { /* ... */ }
|
ALWAYS_INLINE KScopedInterruptEnable() : m_prev_intr_state(KInterruptManager::GetInterruptsEnabledStateAndEnableInterrupts()) { /* ... */ }
|
||||||
ALWAYS_INLINE ~KScopedInterruptEnable() { KInterruptManager::RestoreInterrupts(m_prev_intr_state); }
|
ALWAYS_INLINE ~KScopedInterruptEnable() { KInterruptManager::RestoreInterrupts(m_prev_intr_state); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -61,139 +61,3 @@ _ZN3ams4kern4arch5arm643cpu23SynchronizeAllCoresImplEPii:
|
|||||||
5:
|
5:
|
||||||
stlr wzr, [x0]
|
stlr wzr, [x0]
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
|
||||||
/* ams::kern::arch::arm64::cpu::ClearPageToZero(void *) */
|
|
||||||
.section .text._ZN3ams4kern4arch5arm643cpu19ClearPageToZeroImplEPv, "ax", %progbits
|
|
||||||
.global _ZN3ams4kern4arch5arm643cpu19ClearPageToZeroImplEPv
|
|
||||||
.type _ZN3ams4kern4arch5arm643cpu19ClearPageToZeroImplEPv, %function
|
|
||||||
_ZN3ams4kern4arch5arm643cpu19ClearPageToZeroImplEPv:
|
|
||||||
/* Efficiently clear the page using dc zva. */
|
|
||||||
dc zva, x0
|
|
||||||
add x8, x0, #0x040
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x080
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x0c0
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x100
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x140
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x180
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x1c0
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x200
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x240
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x280
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x2c0
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x300
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x340
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x380
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x3c0
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x400
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x440
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x480
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x4c0
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x500
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x540
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x580
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x5c0
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x600
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x640
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x680
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x6c0
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x700
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x740
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x780
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x7c0
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x800
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x840
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x880
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x8c0
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x900
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x940
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x980
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0x9c0
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xa00
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xa40
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xa80
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xac0
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xb00
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xb40
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xb80
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xbc0
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xc00
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xc40
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xc80
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xcc0
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xd00
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xd40
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xd80
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xdc0
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xe00
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xe40
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xe80
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xec0
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xf00
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xf40
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xf80
|
|
||||||
dc zva, x8
|
|
||||||
add x8, x0, #0xfc0
|
|
||||||
dc zva, x8
|
|
||||||
ret
|
|
||||||
@@ -225,7 +225,7 @@ namespace ams::kern::arch::arm64 {
|
|||||||
if (AMS_UNLIKELY(GetCurrentThread().IsSingleStep())) {
|
if (AMS_UNLIKELY(GetCurrentThread().IsSingleStep())) {
|
||||||
GetCurrentThread().ClearSingleStep();
|
GetCurrentThread().ClearSingleStep();
|
||||||
cpu::MonitorDebugSystemControlRegisterAccessor().SetSoftwareStep(false).Store();
|
cpu::MonitorDebugSystemControlRegisterAccessor().SetSoftwareStep(false).Store();
|
||||||
cpu::EnsureInstructionConsistency();
|
cpu::InstructionMemoryBarrier();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ namespace ams::kern::arch::arm64 {
|
|||||||
if (user_mode) {
|
if (user_mode) {
|
||||||
KThread *cur_thread = GetCurrentThreadPointer();
|
KThread *cur_thread = GetCurrentThreadPointer();
|
||||||
if (cur_thread->IsTerminationRequested()) {
|
if (cur_thread->IsTerminationRequested()) {
|
||||||
KScopedInterruptEnable ei;
|
EnableInterrupts();
|
||||||
cur_thread->Exit();
|
cur_thread->Exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,13 +212,14 @@ namespace ams::kern::arch::arm64 {
|
|||||||
|
|
||||||
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
|
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
|
||||||
|
|
||||||
KScopedInterruptDisable di;
|
|
||||||
|
|
||||||
if (KInterruptController::IsGlobal(irq)) {
|
if (KInterruptController::IsGlobal(irq)) {
|
||||||
|
KScopedInterruptDisable di;
|
||||||
KScopedSpinLock lk(this->GetGlobalInterruptLock());
|
KScopedSpinLock lk(this->GetGlobalInterruptLock());
|
||||||
return this->BindGlobal(handler, irq, core_id, priority, manual_clear, level);
|
return this->BindGlobal(handler, irq, core_id, priority, manual_clear, level);
|
||||||
} else {
|
} else {
|
||||||
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
|
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
|
||||||
|
|
||||||
|
KScopedInterruptDisable di;
|
||||||
return this->BindLocal(handler, irq, priority, manual_clear);
|
return this->BindLocal(handler, irq, priority, manual_clear);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -228,13 +229,16 @@ namespace ams::kern::arch::arm64 {
|
|||||||
|
|
||||||
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
|
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
|
||||||
|
|
||||||
KScopedInterruptDisable di;
|
|
||||||
|
|
||||||
if (KInterruptController::IsGlobal(irq)) {
|
if (KInterruptController::IsGlobal(irq)) {
|
||||||
|
KScopedInterruptDisable di;
|
||||||
|
|
||||||
KScopedSpinLock lk(this->GetGlobalInterruptLock());
|
KScopedSpinLock lk(this->GetGlobalInterruptLock());
|
||||||
return this->UnbindGlobal(irq);
|
return this->UnbindGlobal(irq);
|
||||||
} else {
|
} else {
|
||||||
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
|
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
|
||||||
|
|
||||||
|
KScopedInterruptDisable di;
|
||||||
return this->UnbindLocal(irq);
|
return this->UnbindLocal(irq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,13 +248,15 @@ namespace ams::kern::arch::arm64 {
|
|||||||
|
|
||||||
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
|
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
|
||||||
|
|
||||||
KScopedInterruptDisable di;
|
|
||||||
|
|
||||||
if (KInterruptController::IsGlobal(irq)) {
|
if (KInterruptController::IsGlobal(irq)) {
|
||||||
|
KScopedInterruptDisable di;
|
||||||
KScopedSpinLock lk(this->GetGlobalInterruptLock());
|
KScopedSpinLock lk(this->GetGlobalInterruptLock());
|
||||||
return this->ClearGlobal(irq);
|
return this->ClearGlobal(irq);
|
||||||
} else {
|
} else {
|
||||||
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
|
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
|
||||||
|
|
||||||
|
KScopedInterruptDisable di;
|
||||||
return this->ClearLocal(irq);
|
return this->ClearLocal(irq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,10 +169,10 @@ namespace ams::kern::arch::arm64 {
|
|||||||
m_manager = std::addressof(Kernel::GetSystemPageTableManager());
|
m_manager = std::addressof(Kernel::GetSystemPageTableManager());
|
||||||
|
|
||||||
/* Allocate a page for ttbr. */
|
/* Allocate a page for ttbr. */
|
||||||
|
/* NOTE: It is a postcondition of page table manager allocation that the page is all-zero. */
|
||||||
const u64 asid_tag = (static_cast<u64>(m_asid) << 48ul);
|
const u64 asid_tag = (static_cast<u64>(m_asid) << 48ul);
|
||||||
const KVirtualAddress page = m_manager->Allocate();
|
const KVirtualAddress page = m_manager->Allocate();
|
||||||
MESOSPHERE_ASSERT(page != Null<KVirtualAddress>);
|
MESOSPHERE_ASSERT(page != Null<KVirtualAddress>);
|
||||||
cpu::ClearPageToZero(GetVoidPointer(page));
|
|
||||||
m_ttbr = GetInteger(KPageTableBase::GetLinearMappedPhysicalAddress(page)) | asid_tag;
|
m_ttbr = GetInteger(KPageTableBase::GetLinearMappedPhysicalAddress(page)) | asid_tag;
|
||||||
|
|
||||||
/* Initialize the base page table. */
|
/* Initialize the base page table. */
|
||||||
@@ -1058,7 +1058,7 @@ namespace ams::kern::arch::arm64 {
|
|||||||
auto sw_reserved_bits = PageTableEntry::EncodeSoftwareReservedBits(head_entry->IsHeadMergeDisabled(), head_entry->IsHeadAndBodyMergeDisabled(), tail_entry->IsTailMergeDisabled());
|
auto sw_reserved_bits = PageTableEntry::EncodeSoftwareReservedBits(head_entry->IsHeadMergeDisabled(), head_entry->IsHeadAndBodyMergeDisabled(), tail_entry->IsTailMergeDisabled());
|
||||||
|
|
||||||
/* Merge! */
|
/* Merge! */
|
||||||
PteDataSynchronizationBarrier();
|
/* NOTE: As of 13.1.0, Nintendo does not do: PteDataSynchronizationBarrier(); */
|
||||||
*l1_entry = L1PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, PageTableEntry(entry_template), sw_reserved_bits, false);
|
*l1_entry = L1PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, PageTableEntry(entry_template), sw_reserved_bits, false);
|
||||||
|
|
||||||
/* Note that we updated. */
|
/* Note that we updated. */
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ _ZN3ams4kern3svc25CallReturnFromException64Ev:
|
|||||||
stp x20, x21, [sp, #(EXCEPTION_CONTEXT_X20_X21)]
|
stp x20, x21, [sp, #(EXCEPTION_CONTEXT_X20_X21)]
|
||||||
stp x22, x23, [sp, #(EXCEPTION_CONTEXT_X22_X23)]
|
stp x22, x23, [sp, #(EXCEPTION_CONTEXT_X22_X23)]
|
||||||
stp x24, x25, [sp, #(EXCEPTION_CONTEXT_X24_X25)]
|
stp x24, x25, [sp, #(EXCEPTION_CONTEXT_X24_X25)]
|
||||||
stp x26, x26, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
|
stp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
|
||||||
stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
|
stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
|
||||||
|
|
||||||
/* Call ams::kern::arch::arm64::ReturnFromException(result). */
|
/* Call ams::kern::arch::arm64::ReturnFromException(result). */
|
||||||
|
|||||||
@@ -50,10 +50,6 @@ _ZN3ams4kern3svc26CallSendSyncRequestLight64Ev:
|
|||||||
.global _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev
|
.global _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev
|
||||||
.type _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev, %function
|
.type _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev, %function
|
||||||
_ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev:
|
_ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev:
|
||||||
/* Load x4-x7 from where the svc handler stores them. */
|
|
||||||
ldp x4, x5, [sp, #(8 * 0)]
|
|
||||||
ldp x6, x7, [sp, #(8 * 2)]
|
|
||||||
|
|
||||||
/* Allocate space for the light ipc data. */
|
/* Allocate space for the light ipc data. */
|
||||||
sub sp, sp, #(4 * 8)
|
sub sp, sp, #(4 * 8)
|
||||||
|
|
||||||
@@ -78,13 +74,8 @@ _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev:
|
|||||||
/* Free the stack space for the light ipc data. */
|
/* Free the stack space for the light ipc data. */
|
||||||
add sp, sp, #(4 * 8)
|
add sp, sp, #(4 * 8)
|
||||||
|
|
||||||
/* Save x4-x7 to where the svc handler stores them. */
|
|
||||||
stp x4, x5, [sp, #(8 * 0)]
|
|
||||||
stp x6, x7, [sp, #(8 * 2)]
|
|
||||||
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
|
||||||
/* ams::kern::svc::CallReplyAndReceiveLight64() */
|
/* ams::kern::svc::CallReplyAndReceiveLight64() */
|
||||||
.section .text._ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev, "ax", %progbits
|
.section .text._ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev, "ax", %progbits
|
||||||
.global _ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev
|
.global _ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev
|
||||||
@@ -121,10 +112,6 @@ _ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev:
|
|||||||
.global _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev
|
.global _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev
|
||||||
.type _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev, %function
|
.type _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev, %function
|
||||||
_ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev:
|
_ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev:
|
||||||
/* Load x4-x7 from where the svc handler stores them. */
|
|
||||||
ldp x4, x5, [sp, #(8 * 0)]
|
|
||||||
ldp x6, x7, [sp, #(8 * 2)]
|
|
||||||
|
|
||||||
/* Allocate space for the light ipc data. */
|
/* Allocate space for the light ipc data. */
|
||||||
sub sp, sp, #(4 * 8)
|
sub sp, sp, #(4 * 8)
|
||||||
|
|
||||||
@@ -149,8 +136,4 @@ _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev:
|
|||||||
/* Free the stack space for the light ipc data. */
|
/* Free the stack space for the light ipc data. */
|
||||||
add sp, sp, #(4 * 8)
|
add sp, sp, #(4 * 8)
|
||||||
|
|
||||||
/* Save x4-x7 to where the svc handler stores them. */
|
|
||||||
stp x4, x5, [sp, #(8 * 0)]
|
|
||||||
stp x6, x7, [sp, #(8 * 2)]
|
|
||||||
|
|
||||||
ret
|
ret
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace ams::kern::svc {
|
|||||||
|
|
||||||
/* Set omit-frame-pointer to prevent GCC from emitting MOV X29, SP instructions. */
|
/* Set omit-frame-pointer to prevent GCC from emitting MOV X29, SP instructions. */
|
||||||
#pragma GCC push_options
|
#pragma GCC push_options
|
||||||
#pragma GCC optimize ("-O2")
|
#pragma GCC optimize ("-O3")
|
||||||
#pragma GCC optimize ("omit-frame-pointer")
|
#pragma GCC optimize ("omit-frame-pointer")
|
||||||
|
|
||||||
AMS_SVC_FOREACH_KERN_DEFINITION(DECLARE_SVC_STRUCT, _)
|
AMS_SVC_FOREACH_KERN_DEFINITION(DECLARE_SVC_STRUCT, _)
|
||||||
|
|||||||
@@ -548,11 +548,11 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
|
|
||||||
/* Print the interrupt. */
|
/* 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);
|
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;
|
return (value >> ofs) & 1u;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -656,9 +656,8 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
MESOSPHERE_ASSERT(IsValidPhysicalAddress(table_phys_addr));
|
MESOSPHERE_ASSERT(IsValidPhysicalAddress(table_phys_addr));
|
||||||
Kernel::GetSystemPageTableManager().Open(table_virt_addr, 1);
|
Kernel::GetSystemPageTableManager().Open(table_virt_addr, 1);
|
||||||
|
|
||||||
/* Clear the page and save it. */
|
/* Save the page. Note that it is a pre-condition that the page is cleared, when allocated from the system page table manager. */
|
||||||
/* NOTE: Nintendo does not check the result of StoreDataCache. */
|
/* NOTE: Nintendo does not check the result of StoreDataCache. */
|
||||||
cpu::ClearPageToZero(GetVoidPointer(table_virt_addr));
|
|
||||||
cpu::StoreDataCache(GetVoidPointer(table_virt_addr), PageDirectorySize);
|
cpu::StoreDataCache(GetVoidPointer(table_virt_addr), PageDirectorySize);
|
||||||
g_reserved_table_phys_addr = table_phys_addr;
|
g_reserved_table_phys_addr = table_phys_addr;
|
||||||
|
|
||||||
|
|||||||
@@ -341,7 +341,9 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
|
|
||||||
/* Restore pmu registers. */
|
/* Restore pmu registers. */
|
||||||
cpu::SetPmUserEnrEl0(0);
|
cpu::SetPmUserEnrEl0(0);
|
||||||
cpu::PerformanceMonitorsControlRegisterAccessor().SetEventCounterReset(true).SetCycleCounterReset(true).Store();
|
cpu::PerformanceMonitorsControlRegisterAccessor(0).SetEventCounterReset(true).SetCycleCounterReset(true).Store();
|
||||||
|
cpu::EnsureInstructionConsistency();
|
||||||
|
|
||||||
cpu::SetPmOvsClrEl0(static_cast<u64>(static_cast<u32>(~u32())));
|
cpu::SetPmOvsClrEl0(static_cast<u64>(static_cast<u32>(~u32())));
|
||||||
cpu::SetPmIntEnClrEl1(static_cast<u64>(static_cast<u32>(~u32())));
|
cpu::SetPmIntEnClrEl1(static_cast<u64>(static_cast<u32>(~u32())));
|
||||||
cpu::SetPmCntEnClrEl0(static_cast<u64>(static_cast<u32>(~u32())));
|
cpu::SetPmCntEnClrEl0(static_cast<u64>(static_cast<u32>(~u32())));
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
|
|
||||||
size_t KSystemControl::Init::GetApplicationPoolSize() {
|
size_t KSystemControl::Init::GetApplicationPoolSize() {
|
||||||
/* Get the base pool size. */
|
/* 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()) {
|
switch (GetMemoryArrangeForInit()) {
|
||||||
case smc::MemoryArrangement_4GB:
|
case smc::MemoryArrangement_4GB:
|
||||||
default:
|
default:
|
||||||
@@ -336,7 +336,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
|
|
||||||
size_t KSystemControl::Init::GetAppletPoolSize() {
|
size_t KSystemControl::Init::GetAppletPoolSize() {
|
||||||
/* Get the base pool size. */
|
/* 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()) {
|
switch (GetMemoryArrangeForInit()) {
|
||||||
case smc::MemoryArrangement_4GB:
|
case smc::MemoryArrangement_4GB:
|
||||||
default:
|
default:
|
||||||
@@ -490,7 +490,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
|
|
||||||
|
|
||||||
if (AMS_LIKELY(s_initialized_random_generator)) {
|
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 {
|
} else {
|
||||||
return KSystemControlBase::GenerateUniformRange(min, max, GenerateRandomU64FromSmc);
|
return KSystemControlBase::GenerateUniformRange(min, max, GenerateRandomU64FromSmc);
|
||||||
}
|
}
|
||||||
@@ -680,4 +680,4 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
Kernel::GetMemoryManager().Close(KPageTable::GetHeapPhysicalAddress(address), size / PageSize);
|
Kernel::GetMemoryManager().Close(KPageTable::GetHeapPhysicalAddress(address), size / PageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace ams::kern {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr std::tuple<KMemoryState, const char *> MemoryStateNames[] = {
|
constexpr const std::pair<KMemoryState, const char *> MemoryStateNames[] = {
|
||||||
{KMemoryState_Free , "----- Free -----"},
|
{KMemoryState_Free , "----- Free -----"},
|
||||||
{KMemoryState_Io , "Io "},
|
{KMemoryState_Io , "Io "},
|
||||||
{KMemoryState_Static , "Static "},
|
{KMemoryState_Static , "Static "},
|
||||||
@@ -41,6 +41,7 @@ namespace ams::kern {
|
|||||||
{KMemoryState_Kernel , "Kernel "},
|
{KMemoryState_Kernel , "Kernel "},
|
||||||
{KMemoryState_GeneratedCode , "GeneratedCode "},
|
{KMemoryState_GeneratedCode , "GeneratedCode "},
|
||||||
{KMemoryState_CodeOut , "CodeOut "},
|
{KMemoryState_CodeOut , "CodeOut "},
|
||||||
|
{KMemoryState_Coverage , "Coverage "},
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr const char *GetMemoryStateName(KMemoryState state) {
|
constexpr const char *GetMemoryStateName(KMemoryState state) {
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ namespace ams::kern {
|
|||||||
static_assert(KMemoryManager::Pool_Secure == KMemoryManager::Pool_System);
|
static_assert(KMemoryManager::Pool_Secure == KMemoryManager::Pool_System);
|
||||||
|
|
||||||
/* Get Secure pool size. */
|
/* 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 LegacySecureKernelSize = 8_MB; /* KPageBuffer pages, other small kernel allocations. */
|
||||||
constexpr size_t LegacySecureMiscSize = 1_MB; /* Miscellaneous pages for secure process mapping. */
|
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). */
|
constexpr size_t LegacySecureHeapSize = 24_MB; /* Heap pages for secure process mapping (fs). */
|
||||||
@@ -274,4 +274,4 @@ namespace ams::kern {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
|
||||||
size_t tot_size = cur_size;
|
size_t tot_size = cur_size;
|
||||||
|
|
||||||
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||||
/* Ensure the address is linear mapped. */
|
/* Ensure the address is linear mapped. */
|
||||||
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
|
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 cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
|
||||||
size_t tot_size = cur_size;
|
size_t tot_size = cur_size;
|
||||||
|
|
||||||
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||||
/* Ensure the address is linear mapped. */
|
/* Ensure the address is linear mapped. */
|
||||||
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
|
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 cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
|
||||||
size_t tot_size = cur_size;
|
size_t tot_size = cur_size;
|
||||||
|
|
||||||
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||||
/* Ensure the address is linear mapped. */
|
/* Ensure the address is linear mapped. */
|
||||||
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
|
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 cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
|
||||||
size_t tot_size = cur_size;
|
size_t tot_size = cur_size;
|
||||||
|
|
||||||
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||||
/* Ensure the address is linear mapped. */
|
/* Ensure the address is linear mapped. */
|
||||||
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
|
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 cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
|
||||||
size_t tot_size = cur_size;
|
size_t tot_size = cur_size;
|
||||||
|
|
||||||
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||||
/* Ensure the address is linear mapped. */
|
/* Ensure the address is linear mapped. */
|
||||||
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
|
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 cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
|
||||||
size_t tot_size = cur_size;
|
size_t tot_size = cur_size;
|
||||||
|
|
||||||
auto PerformCopy = [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
auto PerformCopy = [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||||
/* Ensure the address is linear mapped. */
|
/* Ensure the address is linear mapped. */
|
||||||
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
|
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), svc::ResultInvalidCurrentMemory());
|
||||||
|
|
||||||
|
|||||||
@@ -893,7 +893,7 @@ namespace ams::kern {
|
|||||||
size_t KProcess::GetTotalUserPhysicalMemorySize() const {
|
size_t KProcess::GetTotalUserPhysicalMemorySize() const {
|
||||||
/* Get the amount of free and used size. */
|
/* Get the amount of free and used size. */
|
||||||
const size_t free_size = m_resource_limit->GetFreeValue(ams::svc::LimitableResource_PhysicalMemoryMax);
|
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;
|
const size_t max_size = m_max_process_memory;
|
||||||
|
|
||||||
if (used_size + free_size > max_size) {
|
if (used_size + free_size > max_size) {
|
||||||
|
|||||||
@@ -15,10 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
#include <mesosphere.hpp>
|
#include <mesosphere.hpp>
|
||||||
|
|
||||||
namespace ams::kern {
|
#pragma GCC push_options
|
||||||
|
#pragma GCC optimize ("-O3")
|
||||||
|
|
||||||
#pragma GCC push_options
|
namespace ams::kern {
|
||||||
#pragma GCC optimize ("-O3")
|
|
||||||
|
|
||||||
bool KScheduler::s_scheduler_update_needed;
|
bool KScheduler::s_scheduler_update_needed;
|
||||||
KScheduler::LockType KScheduler::s_scheduler_lock;
|
KScheduler::LockType KScheduler::s_scheduler_lock;
|
||||||
@@ -79,13 +79,6 @@ namespace ams::kern {
|
|||||||
RescheduleCurrentCore();
|
RescheduleCurrentCore();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KScheduler::RescheduleOtherCores(u64 cores_needing_scheduling) {
|
|
||||||
if (const u64 core_mask = cores_needing_scheduling & ~(1ul << m_core_id); core_mask != 0) {
|
|
||||||
cpu::DataSynchronizationBarrier();
|
|
||||||
Kernel::GetInterruptManager().SendInterProcessorInterrupt(KInterruptName_Scheduler, core_mask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 KScheduler::UpdateHighestPriorityThread(KThread *highest_thread) {
|
u64 KScheduler::UpdateHighestPriorityThread(KThread *highest_thread) {
|
||||||
if (KThread *prev_highest_thread = m_state.highest_priority_thread; AMS_LIKELY(prev_highest_thread != highest_thread)) {
|
if (KThread *prev_highest_thread = m_state.highest_priority_thread; AMS_LIKELY(prev_highest_thread != highest_thread)) {
|
||||||
if (AMS_LIKELY(prev_highest_thread != nullptr)) {
|
if (AMS_LIKELY(prev_highest_thread != nullptr)) {
|
||||||
@@ -254,6 +247,17 @@ namespace ams::kern {
|
|||||||
|
|
||||||
MESOSPHERE_KTRACE_THREAD_SWITCH(next_thread);
|
MESOSPHERE_KTRACE_THREAD_SWITCH(next_thread);
|
||||||
|
|
||||||
|
#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. */
|
/* Switch the current process, if we're switching processes. */
|
||||||
if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) {
|
if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) {
|
||||||
KProcess::Switch(cur_process, next_process);
|
KProcess::Switch(cur_process, next_process);
|
||||||
@@ -594,6 +598,6 @@ namespace ams::kern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma GCC pop_options
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma GCC pop_options
|
||||||
|
|||||||
@@ -15,10 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
#include <mesosphere.hpp>
|
#include <mesosphere.hpp>
|
||||||
|
|
||||||
namespace ams::kern {
|
#pragma GCC push_options
|
||||||
|
#pragma GCC optimize ("-O3")
|
||||||
|
|
||||||
#pragma GCC push_options
|
namespace ams::kern {
|
||||||
#pragma GCC optimize ("-O3")
|
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
|
|
||||||
@@ -1385,8 +1385,6 @@ namespace ams::kern {
|
|||||||
this->NotifyAvailable(svc::ResultSessionClosed());
|
this->NotifyAvailable(svc::ResultSessionClosed());
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma GCC pop_options
|
|
||||||
|
|
||||||
void KServerSession::Dump() {
|
void KServerSession::Dump() {
|
||||||
MESOSPHERE_ASSERT_THIS();
|
MESOSPHERE_ASSERT_THIS();
|
||||||
|
|
||||||
@@ -1420,3 +1418,5 @@ namespace ams::kern {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma GCC pop_options
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ namespace ams::kern {
|
|||||||
s_initialized_random_generator = true;
|
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. */
|
/* System Initialization. */
|
||||||
@@ -194,7 +194,7 @@ namespace ams::kern {
|
|||||||
KScopedInterruptDisable intr_disable;
|
KScopedInterruptDisable intr_disable;
|
||||||
KScopedSpinLock lk(s_random_lock);
|
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() {
|
u64 KSystemControlBase::GenerateRandomU64() {
|
||||||
@@ -292,4 +292,4 @@ namespace ams::kern {
|
|||||||
Kernel::GetMemoryManager().Close(KPageTable::GetHeapPhysicalAddress(address), size / PageSize);
|
Kernel::GetMemoryManager().Close(KPageTable::GetHeapPhysicalAddress(address), size / PageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1183,7 +1183,7 @@ namespace ams::kern {
|
|||||||
KScopedSchedulerLock sl;
|
KScopedSchedulerLock sl;
|
||||||
|
|
||||||
/* Determine if this is the first termination request. */
|
/* 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. */
|
/* Perform an atomic compare-and-swap from false to true. */
|
||||||
bool expected = false;
|
bool expected = false;
|
||||||
return m_termination_requested.CompareExchangeStrong(expected, true);
|
return m_termination_requested.CompareExchangeStrong(expected, true);
|
||||||
@@ -1315,10 +1315,6 @@ namespace ams::kern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KThreadContext *KThread::GetContextForSchedulerLoop() {
|
|
||||||
return std::addressof(this->GetContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
KThread *KThread::GetThreadFromId(u64 thread_id) {
|
KThread *KThread::GetThreadFromId(u64 thread_id) {
|
||||||
/* Lock the list. */
|
/* Lock the list. */
|
||||||
KThread::ListAccessor accessor;
|
KThread::ListAccessor accessor;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace ams::kern::svc {
|
|||||||
/* Check whether the address is aligned. */
|
/* Check whether the address is aligned. */
|
||||||
const bool aligned = util::IsAligned(phys_addr, PageSize);
|
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. */
|
/* The size must be non-zero. */
|
||||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,80 @@ namespace ams::kern::svc {
|
|||||||
return ResultSuccess();
|
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) {
|
Result GetInfo(u64 *out, ams::svc::InfoType info_type, ams::svc::Handle handle, u64 info_subtype) {
|
||||||
switch (info_type) {
|
switch (info_type) {
|
||||||
case ams::svc::InfoType_CoreMask:
|
case ams::svc::InfoType_CoreMask:
|
||||||
@@ -66,77 +140,34 @@ namespace ams::kern::svc {
|
|||||||
|
|
||||||
/* Get the process from its handle. */
|
/* Get the process from its handle. */
|
||||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(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());
|
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||||
|
|
||||||
switch (info_type) {
|
/* Return the relevant info. */
|
||||||
case ams::svc::InfoType_CoreMask:
|
return GetInfoImpl(out, info_type, process.GetPointerUnsafe());
|
||||||
*out = process->GetCoreMask();
|
#endif
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ams::svc::InfoType_DebuggerAttached:
|
case ams::svc::InfoType_DebuggerAttached:
|
||||||
|
|||||||
@@ -15,10 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
#include <mesosphere.hpp>
|
#include <mesosphere.hpp>
|
||||||
|
|
||||||
namespace ams::kern::svc {
|
#pragma GCC push_options
|
||||||
|
#pragma GCC optimize ("-O3")
|
||||||
|
|
||||||
#pragma GCC push_options
|
namespace ams::kern::svc {
|
||||||
#pragma GCC optimize ("-O3")
|
|
||||||
|
|
||||||
/* ============================= Common ============================= */
|
/* ============================= 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);
|
return ReplyAndReceiveWithUserBuffer(out_index, message_buffer, message_buffer_size, handles, num_handles, reply_target, timeout_ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma GCC pop_options
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma GCC pop_options
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ namespace ams::kern::svc {
|
|||||||
KResourceLimit *process_resource_limit = resource_limit.IsNotNull() ? resource_limit.GetPointerUnsafe() : std::addressof(Kernel::GetSystemResourceLimit());
|
KResourceLimit *process_resource_limit = resource_limit.IsNotNull() ? resource_limit.GetPointerUnsafe() : std::addressof(Kernel::GetSystemResourceLimit());
|
||||||
|
|
||||||
/* Get the pool for the process. */
|
/* 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) {
|
if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
|
||||||
switch (flags & ams::svc::CreateProcessFlag_PoolPartitionMask) {
|
switch (flags & ams::svc::CreateProcessFlag_PoolPartitionMask) {
|
||||||
case ams::svc::CreateProcessFlag_PoolPartitionApplication:
|
case ams::svc::CreateProcessFlag_PoolPartitionApplication:
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ namespace ams::impl {
|
|||||||
AMS_DEFINE_SYSTEM_THREAD(-7, mitm, InitializeThread);
|
AMS_DEFINE_SYSTEM_THREAD(-7, mitm, InitializeThread);
|
||||||
AMS_DEFINE_SYSTEM_THREAD(-1, mitm_sf, QueryServerProcessThread);
|
AMS_DEFINE_SYSTEM_THREAD(-1, mitm_sf, QueryServerProcessThread);
|
||||||
AMS_DEFINE_SYSTEM_THREAD(16, mitm_fs, RomFileSystemInitializeThread);
|
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, DebugThrowThread);
|
||||||
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, IpcServer);
|
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, IpcServer);
|
||||||
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, AsyncPrepareSdCardUpdateTask);
|
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, AsyncPrepareSdCardUpdateTask);
|
||||||
|
|||||||
@@ -118,12 +118,12 @@ namespace ams::fssystem {
|
|||||||
|
|
||||||
/* Copy API. */
|
/* 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);
|
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);
|
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);
|
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);
|
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 EnsureDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path);
|
||||||
Result EnsureParentDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path);
|
Result EnsureParentDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path);
|
||||||
|
|
||||||
template<typename F>
|
template<s64 RetryMilliSeconds = 100>
|
||||||
NX_INLINE Result RetryFinitelyForTargetLocked(F f) {
|
ALWAYS_INLINE Result RetryFinitelyForTargetLocked(auto f) {
|
||||||
/* Retry up to 10 times, 100ms between retries. */
|
/* Retry up to 10 times, 100ms between retries. */
|
||||||
constexpr s32 MaxRetryCount = 10;
|
constexpr s32 MaxRetryCount = 10;
|
||||||
constexpr TimeSpan RetryWaitTime = TimeSpan::FromMilliSeconds(100);
|
constexpr TimeSpan RetryWaitTime = TimeSpan::FromMilliSeconds(RetryMilliSeconds);
|
||||||
|
|
||||||
s32 remaining_retries = MaxRetryCount;
|
s32 remaining_retries = MaxRetryCount;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ namespace ams::hos {
|
|||||||
Version_12_0_3 = ::ams::TargetFirmware_12_0_3,
|
Version_12_0_3 = ::ams::TargetFirmware_12_0_3,
|
||||||
Version_12_1_0 = ::ams::TargetFirmware_12_1_0,
|
Version_12_1_0 = ::ams::TargetFirmware_12_1_0,
|
||||||
Version_13_0_0 = ::ams::TargetFirmware_13_0_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_Current = ::ams::TargetFirmware_Current,
|
Version_Current = ::ams::TargetFirmware_Current,
|
||||||
|
|
||||||
|
|||||||
@@ -62,20 +62,16 @@ namespace ams::settings {
|
|||||||
|
|
||||||
char name[MaxLength];
|
char name[MaxLength];
|
||||||
|
|
||||||
static constexpr LanguageCode Encode(const char *name, size_t name_size) {
|
static constexpr LanguageCode Encode(util::string_view name) {
|
||||||
LanguageCode out{};
|
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];
|
out.name[i] = name[i];
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr LanguageCode Encode(const char *name) {
|
|
||||||
return Encode(name, std::strlen(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<Language Lang>
|
template<Language Lang>
|
||||||
static constexpr inline LanguageCode EncodeLanguage = [] {
|
static constexpr inline LanguageCode EncodeLanguage() {
|
||||||
if constexpr (false) { /* ... */ }
|
if constexpr (false) { /* ... */ }
|
||||||
#define AMS_MATCH_LANGUAGE(lang, enc) else if constexpr (Lang == Language_##lang) { return LanguageCode::Encode(enc); }
|
#define AMS_MATCH_LANGUAGE(lang, enc) else if constexpr (Lang == Language_##lang) { return LanguageCode::Encode(enc); }
|
||||||
AMS_MATCH_LANGUAGE(Japanese, "ja")
|
AMS_MATCH_LANGUAGE(Japanese, "ja")
|
||||||
@@ -98,28 +94,28 @@ namespace ams::settings {
|
|||||||
AMS_MATCH_LANGUAGE(TraditionalChinese, "zh-Hant")
|
AMS_MATCH_LANGUAGE(TraditionalChinese, "zh-Hant")
|
||||||
#undef AMS_MATCH_LANGUAGE
|
#undef AMS_MATCH_LANGUAGE
|
||||||
else { static_assert(Lang != Language_Japanese); }
|
else { static_assert(Lang != Language_Japanese); }
|
||||||
}();
|
}
|
||||||
|
|
||||||
static constexpr inline LanguageCode Encode(const Language language) {
|
static constexpr inline LanguageCode Encode(const Language language) {
|
||||||
constexpr LanguageCode EncodedLanguages[Language_Count] = {
|
constexpr LanguageCode EncodedLanguages[Language_Count] = {
|
||||||
EncodeLanguage<Language_Japanese>,
|
EncodeLanguage<Language_Japanese>(),
|
||||||
EncodeLanguage<Language_AmericanEnglish>,
|
EncodeLanguage<Language_AmericanEnglish>(),
|
||||||
EncodeLanguage<Language_French>,
|
EncodeLanguage<Language_French>(),
|
||||||
EncodeLanguage<Language_German>,
|
EncodeLanguage<Language_German>(),
|
||||||
EncodeLanguage<Language_Italian>,
|
EncodeLanguage<Language_Italian>(),
|
||||||
EncodeLanguage<Language_Spanish>,
|
EncodeLanguage<Language_Spanish>(),
|
||||||
EncodeLanguage<Language_Chinese>,
|
EncodeLanguage<Language_Chinese>(),
|
||||||
EncodeLanguage<Language_Korean>,
|
EncodeLanguage<Language_Korean>(),
|
||||||
EncodeLanguage<Language_Dutch>,
|
EncodeLanguage<Language_Dutch>(),
|
||||||
EncodeLanguage<Language_Portuguese>,
|
EncodeLanguage<Language_Portuguese>(),
|
||||||
EncodeLanguage<Language_Russian>,
|
EncodeLanguage<Language_Russian>(),
|
||||||
EncodeLanguage<Language_Taiwanese>,
|
EncodeLanguage<Language_Taiwanese>(),
|
||||||
EncodeLanguage<Language_BritishEnglish>,
|
EncodeLanguage<Language_BritishEnglish>(),
|
||||||
EncodeLanguage<Language_CanadianFrench>,
|
EncodeLanguage<Language_CanadianFrench>(),
|
||||||
EncodeLanguage<Language_LatinAmericanSpanish>,
|
EncodeLanguage<Language_LatinAmericanSpanish>(),
|
||||||
/* 4.0.0+ */
|
/* 4.0.0+ */
|
||||||
EncodeLanguage<Language_SimplifiedChinese>,
|
EncodeLanguage<Language_SimplifiedChinese>(),
|
||||||
EncodeLanguage<Language_TraditionalChinese>,
|
EncodeLanguage<Language_TraditionalChinese>(),
|
||||||
};
|
};
|
||||||
return EncodedLanguages[language];
|
return EncodedLanguages[language];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,11 +54,11 @@ namespace ams::sf::cmif {
|
|||||||
|
|
||||||
void DisposeImpl();
|
void DisposeImpl();
|
||||||
|
|
||||||
virtual void AddReference() {
|
virtual void AddReference() override {
|
||||||
ServiceObjectImplBase2::AddReferenceImpl();
|
ServiceObjectImplBase2::AddReferenceImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Release() {
|
virtual void Release() override {
|
||||||
if (ServiceObjectImplBase2::ReleaseImpl()) {
|
if (ServiceObjectImplBase2::ReleaseImpl()) {
|
||||||
this->DisposeImpl();
|
this->DisposeImpl();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ namespace ams::sf {
|
|||||||
return lmem::FreeToExpHeap(m_handle, buffer);
|
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);
|
return this == std::addressof(resource);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -76,9 +76,9 @@ namespace ams::sf {
|
|||||||
return lmem::FreeToUnitHeap(m_handle, buffer);
|
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);
|
return this == std::addressof(resource);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ namespace ams::sf {
|
|||||||
return m_standard_allocator->Free(buffer);
|
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);
|
return this == std::addressof(resource);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace ams::sf {
|
|||||||
public:
|
public:
|
||||||
class Object;
|
class Object;
|
||||||
using Allocator = StatelessDummyAllocator;
|
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 {
|
class Object final : private ::ams::sf::impl::ServiceObjectImplBase2, public Base {
|
||||||
NON_COPYABLE(Object);
|
NON_COPYABLE(Object);
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ namespace ams::sm {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr ServiceName Encode(const char *name) {
|
static constexpr ServiceName Encode(util::string_view name) {
|
||||||
return Encode(name, std::strlen(name));
|
return Encode(name.data(), name.size());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ namespace ams::socket {
|
|||||||
|
|
||||||
s32 Shutdown(s32 desc, ShutdownMethod how);
|
s32 Shutdown(s32 desc, ShutdownMethod how);
|
||||||
|
|
||||||
|
s32 Socket(Family domain, Type type, Protocol protocol);
|
||||||
s32 SocketExempt(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);
|
s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace ams::spl::impl {
|
|||||||
|
|
||||||
Result AllocateAesKeySlot(s32 *out_keyslot);
|
Result AllocateAesKeySlot(s32 *out_keyslot);
|
||||||
Result DeallocateAesKeySlot(s32 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();
|
os::SystemEvent *GetAesKeySlotAvailableEvent();
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace ams::time {
|
|||||||
AMS_ASSERT(rhs.IsValid());
|
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) |
|
return (static_cast<u64>(time.year) << 40) |
|
||||||
(static_cast<u64>(time.month) << 32) |
|
(static_cast<u64>(time.month) << 32) |
|
||||||
(static_cast<u64>(time.day) << 24) |
|
(static_cast<u64>(time.day) << 24) |
|
||||||
|
|||||||
@@ -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) \
|
#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>;
|
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) \
|
#define AMS_TIPC_IMPL_DEFINE_INTERFACE(BASECLASS, CLASSNAME, CMD_MACRO) \
|
||||||
class CLASSNAME : public BASECLASS { \
|
class CLASSNAME : public BASECLASS { \
|
||||||
private: \
|
private: \
|
||||||
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_DEFINE_SYNC_METHOD_HOLDER) \
|
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_DEFINE_SYNC_METHOD_HOLDER) \
|
||||||
public: \
|
public: \
|
||||||
CMD_MACRO(CLASSNAME, AMS_TIPC_IMPL_EXTRACT_SYNC_METHOD_ARGUMENTS) \
|
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) \
|
#define AMS_TIPC_IMPL_DEFINE_CONCEPT_HELPERS(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
||||||
|
|||||||
@@ -319,6 +319,9 @@ namespace ams::tipc::impl {
|
|||||||
static constexpr auto OutMessageResultIndex = svc::ipc::MessageBuffer::GetRawDataIndex(OutMessageHeader, OutSpecialHeader);
|
static constexpr auto OutMessageResultIndex = svc::ipc::MessageBuffer::GetRawDataIndex(OutMessageHeader, OutSpecialHeader);
|
||||||
static constexpr auto OutMessageRawDataIndex = OutMessageResultIndex + 1;
|
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. */
|
/* Construction of argument serialization structs. */
|
||||||
private:
|
private:
|
||||||
template<typename>
|
template<typename>
|
||||||
@@ -472,7 +475,7 @@ namespace ams::tipc::impl {
|
|||||||
using OutRawHolderType = OutRawHolder<CommandMeta::OutDataSize, CommandMeta::OutDataAlign, CommandMeta::OutMessageRawDataIndex>;
|
using OutRawHolderType = OutRawHolder<CommandMeta::OutDataSize, CommandMeta::OutDataAlign, CommandMeta::OutMessageRawDataIndex>;
|
||||||
using OutHandleHolderType = OutHandleHolder<CommandMeta::NumOutMoveHandles, CommandMeta::NumOutCopyHandles, CommandMeta::OutMessageHandleIndex>;
|
using OutHandleHolderType = OutHandleHolder<CommandMeta::NumOutMoveHandles, CommandMeta::NumOutCopyHandles, CommandMeta::OutMessageHandleIndex>;
|
||||||
private:
|
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)>;
|
using Value = util::BitPack32::Field<0, BITSIZEOF(util::BitPack32)>;
|
||||||
|
|
||||||
const util::BitPack32 *data = header.GetData();
|
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));
|
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)>;
|
using Value = util::BitPack32::Field<0, BITSIZEOF(util::BitPack32)>;
|
||||||
|
|
||||||
return header.GetHeader()->Get<Value>();
|
return header.GetHeader()->Get<Value>();
|
||||||
|
|||||||
@@ -44,16 +44,17 @@ namespace ams::tipc {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeferrableBase : public impl::DeferrableBaseTag {
|
class DeferrableBaseImpl : public impl::DeferrableBaseTag {
|
||||||
private:
|
private:
|
||||||
DeferralManagerBase *m_deferral_manager;
|
DeferralManagerBase *m_deferral_manager;
|
||||||
ObjectHolder m_object_holder;
|
ObjectHolder m_object_holder;
|
||||||
uintptr_t m_resume_key;
|
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:
|
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) {
|
ALWAYS_INLINE void SetDeferralManager(DeferralManagerBase *manager, os::NativeHandle reply_target, ServiceObjectBase *object) {
|
||||||
m_deferral_manager = manager;
|
m_deferral_manager = manager;
|
||||||
@@ -67,7 +68,7 @@ namespace ams::tipc {
|
|||||||
template<IsResumeKey ResumeKey>
|
template<IsResumeKey ResumeKey>
|
||||||
ALWAYS_INLINE void RegisterRetry(ResumeKey key) {
|
ALWAYS_INLINE void RegisterRetry(ResumeKey key) {
|
||||||
m_resume_key = ConvertToInternalResumeKey(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>
|
template<IsResumeKey ResumeKey, typename F>
|
||||||
@@ -85,11 +86,49 @@ namespace ams::tipc {
|
|||||||
m_resume_key = 0;
|
m_resume_key = 0;
|
||||||
|
|
||||||
/* Restore message buffer. */
|
/* 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. */
|
/* Process the request. */
|
||||||
return port_manager->ProcessDeferredRequest(m_object_holder);
|
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>
|
template<class T>
|
||||||
@@ -100,11 +139,11 @@ namespace ams::tipc {
|
|||||||
NON_MOVEABLE(DeferralManagerBase);
|
NON_MOVEABLE(DeferralManagerBase);
|
||||||
private:
|
private:
|
||||||
size_t m_object_count;
|
size_t m_object_count;
|
||||||
DeferrableBase *m_objects_base[0];
|
DeferrableBaseImpl *m_objects_base[0];
|
||||||
public:
|
public:
|
||||||
ALWAYS_INLINE DeferralManagerBase() : m_object_count(0) { /* ... */ }
|
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. */
|
/* Set ourselves as the manager for the object. */
|
||||||
object.SetDeferralManager(this, reply_target, service_object);
|
object.SetDeferralManager(this, reply_target, service_object);
|
||||||
|
|
||||||
@@ -113,7 +152,7 @@ namespace ams::tipc {
|
|||||||
m_objects_base[m_object_count++] = std::addressof(object);
|
m_objects_base[m_object_count++] = std::addressof(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveObject(DeferrableBase *object) {
|
void RemoveObject(DeferrableBaseImpl *object) {
|
||||||
/* If the object is present, remove it. */
|
/* If the object is present, remove it. */
|
||||||
for (size_t i = 0; i < m_object_count; ++i) {
|
for (size_t i = 0; i < m_object_count; ++i) {
|
||||||
if (m_objects_base[i] == object) {
|
if (m_objects_base[i] == object) {
|
||||||
@@ -148,7 +187,7 @@ namespace ams::tipc {
|
|||||||
};
|
};
|
||||||
static_assert(std::is_standard_layout<DeferralManagerBase>::value);
|
static_assert(std::is_standard_layout<DeferralManagerBase>::value);
|
||||||
|
|
||||||
inline DeferrableBase::~DeferrableBase() {
|
inline DeferrableBaseImpl::~DeferrableBaseImpl() {
|
||||||
AMS_ASSUME(m_deferral_manager != nullptr);
|
AMS_ASSUME(m_deferral_manager != nullptr);
|
||||||
m_deferral_manager->RemoveObject(this);
|
m_deferral_manager->RemoveObject(this);
|
||||||
}
|
}
|
||||||
@@ -156,7 +195,7 @@ namespace ams::tipc {
|
|||||||
template<size_t N> requires (N > 0)
|
template<size_t N> requires (N > 0)
|
||||||
class DeferralManager final : public DeferralManagerBase {
|
class DeferralManager final : public DeferralManagerBase {
|
||||||
private:
|
private:
|
||||||
DeferrableBase *m_objects[N];
|
DeferrableBaseImpl *m_objects[N];
|
||||||
public:
|
public:
|
||||||
DeferralManager();
|
DeferralManager();
|
||||||
private:
|
private:
|
||||||
@@ -167,15 +206,15 @@ namespace ams::tipc {
|
|||||||
return AMS_OFFSETOF(DeferralManagerBase, m_objects_base);
|
return AMS_OFFSETOF(DeferralManagerBase, m_objects_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t N>
|
template<size_t N> requires (N > 0)
|
||||||
consteval size_t DeferralManager<N>::GetObjectPointersOffset() {
|
consteval size_t DeferralManager<N>::GetObjectPointersOffset() {
|
||||||
return AMS_OFFSETOF(DeferralManager<N>, m_objects);
|
return AMS_OFFSETOF(DeferralManager<N>, m_objects);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t N>
|
template<size_t N> requires (N > 0)
|
||||||
inline DeferralManager<N>::DeferralManager() : DeferralManagerBase() {
|
inline DeferralManager<N>::DeferralManager() : DeferralManagerBase() {
|
||||||
static_assert(GetObjectPointersOffset() == GetObjectPointersOffsetBase());
|
static_assert(GetObjectPointersOffset() == GetObjectPointersOffsetBase());
|
||||||
static_assert(sizeof(DeferralManager<N>) == sizeof(DeferralManagerBase) + N * sizeof(DeferrableBase *));
|
static_assert(sizeof(DeferralManager<N>) == sizeof(DeferralManagerBase) + N * sizeof(DeferrableBaseImpl *));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -686,7 +686,7 @@ namespace ams::tipc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Try to reply/receive. */
|
/* Try to reply/receive. */
|
||||||
const Result result = [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
const Result result = [&]() ALWAYS_INLINE_LAMBDA -> Result {
|
||||||
os::MultiWaitHolderType *signaled_holder = nullptr;
|
os::MultiWaitHolderType *signaled_holder = nullptr;
|
||||||
ON_SCOPE_EXIT { AMS_ABORT_UNLESS(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));
|
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;
|
using PortManager = PortManagerImpl;
|
||||||
private:
|
private:
|
||||||
PortManager m_port_manager;
|
PortManager m_port_manager;
|
||||||
PortInfo::Allocator m_port_allocator;
|
typename PortInfo::Allocator m_port_allocator;
|
||||||
public:
|
public:
|
||||||
constexpr ServerManagerImpl() : m_port_manager(), m_port_allocator() { /* ... */ }
|
constexpr ServerManagerImpl() : m_port_manager(), m_port_allocator() { /* ... */ }
|
||||||
|
|
||||||
|
|||||||
@@ -188,6 +188,12 @@ namespace ams::boot2 {
|
|||||||
return enable_htc != 0;
|
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() {
|
bool IsAtmosphereLogManagerEnabled() {
|
||||||
/* If htc is enabled, ams log manager is enabled. */
|
/* If htc is enabled, ams log manager is enabled. */
|
||||||
if (IsHtcEnabled()) {
|
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::Htc, ncm::StorageId::None), 0);
|
||||||
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Cs, 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);
|
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 {
|
} else {
|
||||||
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Dmnt, ncm::StorageId::None), 0);
|
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Dmnt, ncm::StorageId::None), 0);
|
||||||
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::BuiltInSystem), 0);
|
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::BuiltInSystem), 0);
|
||||||
|
|||||||
@@ -89,9 +89,8 @@ namespace ams::fs {
|
|||||||
Result DeleteSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) {
|
Result DeleteSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) {
|
||||||
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id);
|
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id);
|
||||||
|
|
||||||
/* TODO: Libnx binding for DeleteSaveDataFileSystemBySaveDataAttribute */
|
static_assert(sizeof(attribute) == sizeof(::FsSaveDataAttribute));
|
||||||
AMS_UNUSED(space_id, attribute);
|
return fsDeleteSaveDataFileSystemBySaveDataAttribute(static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute)));
|
||||||
AMS_ABORT();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetSaveDataFlags(u32 *out, SaveDataId id) {
|
Result GetSaveDataFlags(u32 *out, SaveDataId id) {
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ namespace ams::htclow::ctrl {
|
|||||||
/* Lock ourselves. */
|
/* Lock ourselves. */
|
||||||
std::scoped_lock lk(m_mutex);
|
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) {
|
for (auto i = 0; i < num_supported; ++i) {
|
||||||
if (channel.module_id == supported[i].module_id && channel.channel_id == supported[i].channel_id) {
|
if (channel.module_id == supported[i].module_id && channel.channel_id == supported[i].channel_id) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ extern "C" {
|
|||||||
constinit u32 __nx_fs_num_sessions = 1;
|
constinit u32 __nx_fs_num_sessions = 1;
|
||||||
constinit u32 __nx_applet_type = AppletType_None;
|
constinit u32 __nx_applet_type = AppletType_None;
|
||||||
|
|
||||||
|
constinit bool __nx_fsdev_support_cwd = false;
|
||||||
|
|
||||||
extern int __system_argc;
|
extern int __system_argc;
|
||||||
extern char** __system_argv;
|
extern char** __system_argv;
|
||||||
|
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ namespace ams::ncm {
|
|||||||
out_orphaned[i] = true;
|
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. */
|
/* Check if any input content ids match our found content id. */
|
||||||
for (size_t i = 0; i < list.GetSize(); i++) {
|
for (size_t i = 0; i < list.GetSize(); i++) {
|
||||||
if (list[i] == id) {
|
if (list[i] == id) {
|
||||||
|
|||||||
@@ -52,6 +52,26 @@ namespace ams::os::impl {
|
|||||||
/* Get the thread impl object from libnx. */
|
/* Get the thread impl object from libnx. */
|
||||||
ThreadImpl *thread_impl = ::threadGetSelf();
|
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. */
|
/* Get the thread priority. */
|
||||||
s32 horizon_priority;
|
s32 horizon_priority;
|
||||||
R_ABORT_UNLESS(svc::GetThreadPriority(std::addressof(horizon_priority), thread_impl->handle));
|
R_ABORT_UNLESS(svc::GetThreadPriority(std::addressof(horizon_priority), thread_impl->handle));
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace ams::osdbg::impl {
|
|||||||
static_assert(AMS_OFFSETOF(ThreadLocalRegionIlp32, tls) == 0x1C0);
|
static_assert(AMS_OFFSETOF(ThreadLocalRegionIlp32, tls) == 0x1C0);
|
||||||
|
|
||||||
struct LibnxThreadVars {
|
struct LibnxThreadVars {
|
||||||
static constexpr u32 Magic = util::FourCC<'!','T','V','$'>::Code;
|
static constexpr u32 Magic = util::ReverseFourCC<'!','T','V','$'>::Code;
|
||||||
|
|
||||||
u32 magic;
|
u32 magic;
|
||||||
::Handle handle;
|
::Handle handle;
|
||||||
@@ -69,11 +69,11 @@ namespace ams::osdbg::impl {
|
|||||||
volatile u16 disable_counter;
|
volatile u16 disable_counter;
|
||||||
volatile u16 interrupt_flag;
|
volatile u16 interrupt_flag;
|
||||||
u32 reserved0;
|
u32 reserved0;
|
||||||
u64 tls[(0x1E0 - 0x108) / sizeof(u64)];
|
u64 tls[(0x200 - sizeof(LibnxThreadVars) - 0x108) / sizeof(u64)];
|
||||||
LibnxThreadVars thread_vars;
|
LibnxThreadVars thread_vars;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(ThreadLocalRegionLibnx) == sizeof(svc::ThreadLocalRegion));
|
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 {
|
struct LibnxThreadEntryArgs {
|
||||||
u64 t;
|
u64 t;
|
||||||
|
|||||||
@@ -50,9 +50,9 @@ namespace ams::osdbg {
|
|||||||
} else {
|
} else {
|
||||||
/* Special-case libnx threads. */
|
/* Special-case libnx threads. */
|
||||||
if (thread_info->_thread_type_type == ThreadTypeType_Libnx) {
|
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 {
|
} 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();
|
return ResultSuccess();
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ namespace ams::socket::impl {
|
|||||||
|
|
||||||
s32 Shutdown(s32 desc, ShutdownMethod how);
|
s32 Shutdown(s32 desc, ShutdownMethod how);
|
||||||
|
|
||||||
|
s32 Socket(Family domain, Type type, Protocol protocol);
|
||||||
s32 SocketExempt(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);
|
s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len);
|
||||||
|
|||||||
@@ -206,6 +206,8 @@ namespace ams::socket::impl {
|
|||||||
|
|
||||||
/* TODO: socket::resolver::EnableResolverCalls()? Not necessary in our case (htc), but consider calling it. */
|
/* TODO: socket::resolver::EnableResolverCalls()? Not necessary in our case (htc), but consider calling it. */
|
||||||
|
|
||||||
|
g_initialized = true;
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,6 +424,22 @@ namespace ams::socket::impl {
|
|||||||
return result;
|
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) {
|
s32 SocketExempt(Family domain, Type type, Protocol protocol) {
|
||||||
/* Check pre-conditions. */
|
/* Check pre-conditions. */
|
||||||
AMS_ABORT_UNLESS(IsInitialized());
|
AMS_ABORT_UNLESS(IsInitialized());
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ namespace ams::socket {
|
|||||||
return impl::Shutdown(desc, how);
|
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) {
|
s32 SocketExempt(Family domain, Type type, Protocol protocol) {
|
||||||
return impl::SocketExempt(domain, type, protocol);
|
return impl::SocketExempt(domain, type, protocol);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -729,9 +729,10 @@ namespace ams::spl::impl {
|
|||||||
return ResultSuccess();
|
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)) {
|
if (g_is_physical_keyslot_allowed && IsPhysicalAesKeySlot(keyslot)) {
|
||||||
*out_index = keyslot;
|
*out_index = keyslot;
|
||||||
|
*out_virtual = false;
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -740,7 +741,8 @@ namespace ams::spl::impl {
|
|||||||
const s32 index = GetVirtualAesKeySlotIndex(keyslot);
|
const s32 index = GetVirtualAesKeySlotIndex(keyslot);
|
||||||
R_UNLESS(g_is_aes_keyslot_allocated[index], spl::ResultInvalidKeySlot());
|
R_UNLESS(g_is_aes_keyslot_allocated[index], spl::ResultInvalidKeySlot());
|
||||||
|
|
||||||
*out_index = index;
|
*out_index = index;
|
||||||
|
*out_virtual = true;
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,9 @@
|
|||||||
#include "sprofile_srv_i_profile_importer.hpp"
|
#include "sprofile_srv_i_profile_importer.hpp"
|
||||||
|
|
||||||
#define AMS_SPROFILE_I_SPROFILE_SERVICE_FOR_BG_AGENT_INTERFACE_INFO(C, H) \
|
#define AMS_SPROFILE_I_SPROFILE_SERVICE_FOR_BG_AGENT_INTERFACE_INFO(C, H) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 100, Result, OpenProfileImporter, (sf::Out<sf::SharedPointer<::ams::sprofile::IProfileImporter>> out), (out)) \
|
AMS_SF_METHOD_INFO(C, H, 100, Result, OpenProfileImporter, (sf::Out<sf::SharedPointer<::ams::sprofile::IProfileImporter>> out), (out)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 200, Result, GetMetadataEntryData, (sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ProfileMetadataEntryData> &out, const sprofile::srv::ProfileMetadataForImportMetadata &arg), (out_count, out, arg)) \
|
AMS_SF_METHOD_INFO(C, H, 200, Result, GetImportableProfileUrls, (sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ProfileUrl> &out, const sprofile::srv::ProfileMetadataForImportMetadata &arg), (out_count, out, arg)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 201, Result, IsUpdateNeeded, (sf::Out<bool> out, sprofile::Identifier revision_key), (out, revision_key)) \
|
AMS_SF_METHOD_INFO(C, H, 201, Result, IsUpdateNeeded, (sf::Out<bool> out, sprofile::Identifier revision_key), (out, revision_key)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 2000, Result, Reset, (), ())
|
AMS_SF_METHOD_INFO(C, H, 2000, Result, Reset, (), ())
|
||||||
|
|
||||||
AMS_SF_DEFINE_INTERFACE(ams::sprofile, ISprofileServiceForBgAgent, AMS_SPROFILE_I_SPROFILE_SERVICE_FOR_BG_AGENT_INTERFACE_INFO)
|
AMS_SF_DEFINE_INTERFACE(ams::sprofile, ISprofileServiceForBgAgent, AMS_SPROFILE_I_SPROFILE_SERVICE_FOR_BG_AGENT_INTERFACE_INFO)
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ namespace ams::sprofile::srv {
|
|||||||
ProfileManager *m_manager;
|
ProfileManager *m_manager;
|
||||||
public:
|
public:
|
||||||
ProfileImporterImpl(ProfileManager *manager) : m_manager(manager) { /* ... */ }
|
ProfileImporterImpl(ProfileManager *manager) : m_manager(manager) { /* ... */ }
|
||||||
|
|
||||||
|
~ProfileImporterImpl() {
|
||||||
|
m_manager->CloseProfileImporter();
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
Result ImportProfile(const sprofile::srv::ProfileDataForImportData &import);
|
Result ImportProfile(const sprofile::srv::ProfileDataForImportData &import);
|
||||||
Result Commit();
|
Result Commit();
|
||||||
|
|||||||
@@ -151,6 +151,16 @@ namespace ams::sprofile::srv {
|
|||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProfileManager::CloseProfileImporter() {
|
||||||
|
/* Acquire locks. */
|
||||||
|
std::scoped_lock lk1(m_profile_importer_mutex);
|
||||||
|
std::scoped_lock lk2(m_general_mutex);
|
||||||
|
std::scoped_lock lk3(m_fs_mutex);
|
||||||
|
|
||||||
|
/* Close our importer. */
|
||||||
|
m_profile_importer = util::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
Result ProfileManager::ImportProfile(const sprofile::srv::ProfileDataForImportData &import) {
|
Result ProfileManager::ImportProfile(const sprofile::srv::ProfileDataForImportData &import) {
|
||||||
/* Acquire locks. */
|
/* Acquire locks. */
|
||||||
std::scoped_lock lk1(m_profile_importer_mutex);
|
std::scoped_lock lk1(m_profile_importer_mutex);
|
||||||
@@ -165,6 +175,8 @@ namespace ams::sprofile::srv {
|
|||||||
/* Check that the metadata we're importing has a valid hash. */
|
/* Check that the metadata we're importing has a valid hash. */
|
||||||
{
|
{
|
||||||
crypto::Md5Generator md5;
|
crypto::Md5Generator md5;
|
||||||
|
md5.Initialize();
|
||||||
|
|
||||||
md5.Update(std::addressof(import.header), sizeof(import.header));
|
md5.Update(std::addressof(import.header), sizeof(import.header));
|
||||||
md5.Update(std::addressof(import.data), sizeof(import.data) - sizeof(import.data.entries[0]) * (util::size(import.data.entries) - std::min<size_t>(import.data.num_entries, util::size(import.data.entries))));
|
md5.Update(std::addressof(import.data), sizeof(import.data) - sizeof(import.data.entries[0]) * (util::size(import.data.entries) - std::min<size_t>(import.data.num_entries, util::size(import.data.entries))));
|
||||||
|
|
||||||
@@ -258,9 +270,11 @@ namespace ams::sprofile::srv {
|
|||||||
/* Check that the metadata we're importing has a valid hash. */
|
/* Check that the metadata we're importing has a valid hash. */
|
||||||
{
|
{
|
||||||
crypto::Md5Generator md5;
|
crypto::Md5Generator md5;
|
||||||
|
md5.Initialize();
|
||||||
|
|
||||||
md5.Update(std::addressof(import.header), sizeof(import.header));
|
md5.Update(std::addressof(import.header), sizeof(import.header));
|
||||||
md5.Update(std::addressof(import.metadata), sizeof(import.metadata));
|
md5.Update(std::addressof(import.metadata), sizeof(import.metadata));
|
||||||
md5.Update(std::addressof(import.entries), sizeof(import.entries[0]) * std::min<size_t>(import.metadata.num_entries, util::size(import.metadata.entries)));
|
md5.Update(std::addressof(import.profile_urls), sizeof(import.profile_urls[0]) * std::min<size_t>(import.metadata.num_entries, util::size(import.metadata.entries)));
|
||||||
|
|
||||||
u8 hash[crypto::Md5Generator::HashSize];
|
u8 hash[crypto::Md5Generator::HashSize];
|
||||||
md5.GetHash(hash, sizeof(hash));
|
md5.GetHash(hash, sizeof(hash));
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ namespace ams::sprofile::srv {
|
|||||||
Result ResetSaveData();
|
Result ResetSaveData();
|
||||||
|
|
||||||
Result OpenProfileImporter();
|
Result OpenProfileImporter();
|
||||||
|
void CloseProfileImporter();
|
||||||
|
|
||||||
Result ImportProfile(const sprofile::srv::ProfileDataForImportData &data);
|
Result ImportProfile(const sprofile::srv::ProfileDataForImportData &data);
|
||||||
Result Commit();
|
Result Commit();
|
||||||
Result ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data);
|
Result ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace ams::sprofile::srv {
|
|||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ServiceForBgAgent::GetMetadataEntryData(sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ProfileMetadataEntryData> &out, const sprofile::srv::ProfileMetadataForImportMetadata &arg) {
|
Result ServiceForBgAgent::GetImportableProfileUrls(sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ProfileUrl> &out, const sprofile::srv::ProfileMetadataForImportMetadata &arg) {
|
||||||
/* Check size. */
|
/* Check size. */
|
||||||
R_UNLESS(out.GetSize() >= arg.metadata.num_entries, sprofile::ResultInvalidArgument());
|
R_UNLESS(out.GetSize() >= arg.metadata.num_entries, sprofile::ResultInvalidArgument());
|
||||||
|
|
||||||
@@ -42,25 +42,31 @@ namespace ams::sprofile::srv {
|
|||||||
sprofile::srv::ProfileMetadata primary_metadata;
|
sprofile::srv::ProfileMetadata primary_metadata;
|
||||||
R_TRY_CATCH(m_profile_manager->LoadPrimaryMetadata(std::addressof(primary_metadata))) {
|
R_TRY_CATCH(m_profile_manager->LoadPrimaryMetadata(std::addressof(primary_metadata))) {
|
||||||
R_CATCH(fs::ResultPathNotFound) {
|
R_CATCH(fs::ResultPathNotFound) {
|
||||||
/* If we have no metadata, we can't get any entries. */
|
/* It's okay if we have no primary metadata -- this means that all profiles are importable. */
|
||||||
*out_count = 0;
|
primary_metadata.num_entries = 0;
|
||||||
return ResultSuccess();
|
|
||||||
}
|
}
|
||||||
} R_END_TRY_CATCH;
|
} R_END_TRY_CATCH;
|
||||||
|
|
||||||
/* Copy matching entries. */
|
/* We want to return the set of profiles that can be imported, which is just the profiles we don't already have. */
|
||||||
u32 count = 0;
|
u32 count = 0;
|
||||||
for (u32 i = 0; i < arg.metadata.num_entries; ++i) {
|
for (u32 i = 0; i < arg.metadata.num_entries; ++i) {
|
||||||
const auto &arg_entry = arg.metadata.entries[i];
|
const auto &arg_entry = arg.metadata.entries[i];
|
||||||
|
|
||||||
|
/* Check if we have the entry. */
|
||||||
|
bool have_entry = false;
|
||||||
for (u32 j = 0; j < primary_metadata.num_entries; ++j) {
|
for (u32 j = 0; j < primary_metadata.num_entries; ++j) {
|
||||||
const auto &pri_entry = primary_metadata.entries[j];
|
const auto &pri_entry = primary_metadata.entries[j];
|
||||||
|
|
||||||
if (pri_entry.identifier_0 == arg_entry.identifier_0 && pri_entry.identifier_1 == arg_entry.identifier_1) {
|
if (pri_entry.identifier_0 == arg_entry.identifier_0 && pri_entry.identifier_1 == arg_entry.identifier_1) {
|
||||||
out[count++] = arg.entries[i];
|
have_entry = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If we don't already have the entry, it's importable -- copy it out. */
|
||||||
|
if (!have_entry) {
|
||||||
|
out[count++] = arg.profile_urls[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set output count. */
|
/* Set output count. */
|
||||||
@@ -68,6 +74,7 @@ namespace ams::sprofile::srv {
|
|||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ServiceForBgAgent::IsUpdateNeeded(sf::Out<bool> out, Identifier revision_key) {
|
Result ServiceForBgAgent::IsUpdateNeeded(sf::Out<bool> out, Identifier revision_key) {
|
||||||
/* Load primary metadata. */
|
/* Load primary metadata. */
|
||||||
bool loaded_metadata = true;
|
bool loaded_metadata = true;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace ams::sprofile::srv {
|
|||||||
constexpr ServiceForBgAgent(MemoryResource *mr, ProfileManager *pm) : m_memory_resource(mr), m_profile_manager(pm) { /* ... */ }
|
constexpr ServiceForBgAgent(MemoryResource *mr, ProfileManager *pm) : m_memory_resource(mr), m_profile_manager(pm) { /* ... */ }
|
||||||
public:
|
public:
|
||||||
Result OpenProfileImporter(sf::Out<sf::SharedPointer<::ams::sprofile::IProfileImporter>> out);
|
Result OpenProfileImporter(sf::Out<sf::SharedPointer<::ams::sprofile::IProfileImporter>> out);
|
||||||
Result GetMetadataEntryData(sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ProfileMetadataEntryData> &out, const sprofile::srv::ProfileMetadataForImportMetadata &arg);
|
Result GetImportableProfileUrls(sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ProfileUrl> &out, const sprofile::srv::ProfileMetadataForImportMetadata &arg);
|
||||||
Result IsUpdateNeeded(sf::Out<bool> out, Identifier revision_key);
|
Result IsUpdateNeeded(sf::Out<bool> out, Identifier revision_key);
|
||||||
Result Reset();
|
Result Reset();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ namespace ams::sprofile::srv {
|
|||||||
static_assert(util::is_pod<ProfileDataEntry>::value);
|
static_assert(util::is_pod<ProfileDataEntry>::value);
|
||||||
static_assert(sizeof(ProfileDataEntry) == 0x10);
|
static_assert(sizeof(ProfileDataEntry) == 0x10);
|
||||||
|
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileDataEntry, key) == 0x00);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileDataEntry, type) == 0x07);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileDataEntry, value_s64) == 0x08);
|
||||||
|
|
||||||
struct ProfileData {
|
struct ProfileData {
|
||||||
u32 num_entries;
|
u32 num_entries;
|
||||||
u8 unk_04[0x0C];
|
u8 unk_04[0x0C];
|
||||||
@@ -55,6 +59,11 @@ namespace ams::sprofile::srv {
|
|||||||
static_assert(util::is_pod<ProfileData>::value);
|
static_assert(util::is_pod<ProfileData>::value);
|
||||||
static_assert(sizeof(ProfileData) == 0x4000);
|
static_assert(sizeof(ProfileData) == 0x4000);
|
||||||
|
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileData, num_entries) == 0x00);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileData, unk_04) == 0x04);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileData, unk_10) == 0x10);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileData, entries) == 0x30);
|
||||||
|
|
||||||
struct ServiceProfile {
|
struct ServiceProfile {
|
||||||
Identifier name;
|
Identifier name;
|
||||||
ProfileData data;
|
ProfileData data;
|
||||||
@@ -62,6 +71,9 @@ namespace ams::sprofile::srv {
|
|||||||
static_assert(util::is_pod<ServiceProfile>::value);
|
static_assert(util::is_pod<ServiceProfile>::value);
|
||||||
static_assert(sizeof(ServiceProfile) == 0x4008);
|
static_assert(sizeof(ServiceProfile) == 0x4008);
|
||||||
|
|
||||||
|
static_assert(AMS_OFFSETOF(ServiceProfile, name) == 0x00);
|
||||||
|
static_assert(AMS_OFFSETOF(ServiceProfile, data) == 0x08);
|
||||||
|
|
||||||
struct ProfileDataForImportData : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
|
struct ProfileDataForImportData : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
|
||||||
struct {
|
struct {
|
||||||
Identifier identifier_0;
|
Identifier identifier_0;
|
||||||
@@ -77,6 +89,11 @@ namespace ams::sprofile::srv {
|
|||||||
static_assert(util::is_pod<ProfileDataForImportData>::value);
|
static_assert(util::is_pod<ProfileDataForImportData>::value);
|
||||||
static_assert(sizeof(ProfileDataForImportData) == 0x4400);
|
static_assert(sizeof(ProfileDataForImportData) == 0x4400);
|
||||||
|
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileDataForImportData, header) == 0x00);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileDataForImportData, hash) == 0x30);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileDataForImportData, data) == 0x40);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileDataForImportData, unk_4040) == 0x4040);
|
||||||
|
|
||||||
struct ProfileMetadataEntry {
|
struct ProfileMetadataEntry {
|
||||||
Identifier identifier_0;
|
Identifier identifier_0;
|
||||||
Identifier identifier_1;
|
Identifier identifier_1;
|
||||||
@@ -85,22 +102,34 @@ namespace ams::sprofile::srv {
|
|||||||
static_assert(util::is_pod<ProfileMetadataEntry>::value);
|
static_assert(util::is_pod<ProfileMetadataEntry>::value);
|
||||||
static_assert(sizeof(ProfileMetadataEntry) == 0x40);
|
static_assert(sizeof(ProfileMetadataEntry) == 0x40);
|
||||||
|
|
||||||
struct ProfileMetadataEntryData : public sf::PrefersMapAliasTransferMode {
|
static_assert(AMS_OFFSETOF(ProfileMetadataEntry, identifier_0) == 0x00);
|
||||||
u8 unk[0x100];
|
static_assert(AMS_OFFSETOF(ProfileMetadataEntry, identifier_1) == 0x07);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileMetadataEntry, unk_0E) == 0x0E);
|
||||||
|
|
||||||
|
struct ProfileUrl : public sf::PrefersMapAliasTransferMode {
|
||||||
|
char url[0x100];
|
||||||
};
|
};
|
||||||
static_assert(util::is_pod<ProfileMetadataEntryData>::value);
|
static_assert(util::is_pod<ProfileUrl>::value);
|
||||||
static_assert(sizeof(ProfileMetadataEntryData) == 0x100);
|
static_assert(sizeof(ProfileUrl) == 0x100);
|
||||||
|
|
||||||
struct ProfileMetadata {
|
struct ProfileMetadata {
|
||||||
u32 num_entries;
|
u32 num_entries;
|
||||||
u32 unk_04;
|
u32 unk_04;
|
||||||
Identifier revision_key;
|
Identifier revision_key;
|
||||||
|
u8 unk_0F[0x1];
|
||||||
u8 unk_10[0x30];
|
u8 unk_10[0x30];
|
||||||
ProfileMetadataEntry entries[50];
|
ProfileMetadataEntry entries[50];
|
||||||
};
|
};
|
||||||
static_assert(util::is_pod<ProfileMetadata>::value);
|
static_assert(util::is_pod<ProfileMetadata>::value);
|
||||||
static_assert(sizeof(ProfileMetadata) == 0xCC0);
|
static_assert(sizeof(ProfileMetadata) == 0xCC0);
|
||||||
|
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileMetadata, num_entries) == 0x00);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileMetadata, unk_04) == 0x04);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileMetadata, revision_key) == 0x08);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileMetadata, unk_0F) == 0x0F);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileMetadata, unk_10) == 0x10);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileMetadata, entries) == 0x40);
|
||||||
|
|
||||||
struct ProfileMetadataForImportMetadata : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
|
struct ProfileMetadataForImportMetadata : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
|
||||||
struct {
|
struct {
|
||||||
u32 version;
|
u32 version;
|
||||||
@@ -108,10 +137,16 @@ namespace ams::sprofile::srv {
|
|||||||
} header;
|
} header;
|
||||||
u8 hash[crypto::Md5Generator::HashSize];
|
u8 hash[crypto::Md5Generator::HashSize];
|
||||||
ProfileMetadata metadata;
|
ProfileMetadata metadata;
|
||||||
ProfileMetadataEntryData entries[50];
|
ProfileUrl profile_urls[50];
|
||||||
u8 unk[0x8000 - 0x3EF0];
|
u8 unk_3EF0[0x8000 - 0x3EF0];
|
||||||
};
|
};
|
||||||
static_assert(util::is_pod<ProfileMetadataForImportMetadata>::value);
|
static_assert(util::is_pod<ProfileMetadataForImportMetadata>::value);
|
||||||
static_assert(sizeof(ProfileMetadataForImportMetadata) == 0x8000);
|
static_assert(sizeof(ProfileMetadataForImportMetadata) == 0x8000);
|
||||||
|
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileMetadataForImportMetadata, header) == 0x00);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileMetadataForImportMetadata, hash) == 0x20);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileMetadataForImportMetadata, metadata) == 0x30);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileMetadataForImportMetadata, profile_urls) == 0xCF0);
|
||||||
|
static_assert(AMS_OFFSETOF(ProfileMetadataForImportMetadata, unk_3EF0) == 0x3EF0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,10 @@
|
|||||||
|
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 1
|
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 1
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 2
|
#define ATMOSPHERE_RELEASE_VERSION_MINOR 2
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 1
|
#define ATMOSPHERE_RELEASE_VERSION_MICRO 5
|
||||||
|
|
||||||
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
#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_MAJOR 13
|
||||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 1
|
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 2
|
||||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0
|
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0
|
||||||
|
|||||||
@@ -65,8 +65,9 @@
|
|||||||
#define ATMOSPHERE_TARGET_FIRMWARE_12_1_0 ATMOSPHERE_TARGET_FIRMWARE(12, 1, 0)
|
#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_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_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_CURRENT ATMOSPHERE_TARGET_FIRMWARE_13_1_0
|
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_13_2_0
|
||||||
|
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0)
|
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0)
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT
|
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT
|
||||||
@@ -124,6 +125,7 @@ namespace ams {
|
|||||||
TargetFirmware_12_1_0 = ATMOSPHERE_TARGET_FIRMWARE_12_1_0,
|
TargetFirmware_12_1_0 = ATMOSPHERE_TARGET_FIRMWARE_12_1_0,
|
||||||
TargetFirmware_13_0_0 = ATMOSPHERE_TARGET_FIRMWARE_13_0_0,
|
TargetFirmware_13_0_0 = ATMOSPHERE_TARGET_FIRMWARE_13_0_0,
|
||||||
TargetFirmware_13_1_0 = ATMOSPHERE_TARGET_FIRMWARE_13_1_0,
|
TargetFirmware_13_1_0 = ATMOSPHERE_TARGET_FIRMWARE_13_1_0,
|
||||||
|
TargetFirmware_13_2_0 = ATMOSPHERE_TARGET_FIRMWARE_13_2_0,
|
||||||
|
|
||||||
TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT,
|
TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT,
|
||||||
|
|
||||||
|
|||||||
@@ -177,7 +177,11 @@ namespace ams {
|
|||||||
static_assert(Value != Base::SuccessValue, "Value != Base::SuccessValue");
|
static_assert(Value != Base::SuccessValue, "Value != Base::SuccessValue");
|
||||||
public:
|
public:
|
||||||
constexpr ALWAYS_INLINE operator Result() const { return MakeResult(Value); }
|
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 IsSuccess() const { return false; }
|
||||||
constexpr ALWAYS_INLINE bool IsFailure() const { return !this->IsSuccess(); }
|
constexpr ALWAYS_INLINE bool IsFailure() const { return !this->IsSuccess(); }
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ namespace ams::svc {
|
|||||||
/* Thread types. */
|
/* Thread types. */
|
||||||
using ThreadFunc = ams::svc::Address;
|
using ThreadFunc = ams::svc::Address;
|
||||||
|
|
||||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
#if defined(ATMOSPHERE_ARCH_ARM_V8A)
|
||||||
|
|
||||||
struct ThreadContext {
|
struct ThreadContext {
|
||||||
u64 r[29];
|
u64 r[29];
|
||||||
@@ -268,7 +268,7 @@ namespace ams::svc {
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(ThreadContext) == 0x320);
|
static_assert(sizeof(ThreadContext) == 0x320);
|
||||||
|
|
||||||
#elif defined(ATMOSPHERE_ARCH_ARM)
|
#elif defined(ATMOSPHERE_ARCH_ARM_V7A)
|
||||||
|
|
||||||
struct ThreadContext {
|
struct ThreadContext {
|
||||||
u32 r[13];
|
u32 r[13];
|
||||||
@@ -282,9 +282,14 @@ namespace ams::svc {
|
|||||||
u32 fpexc;
|
u32 fpexc;
|
||||||
u32 tpidr;
|
u32 tpidr;
|
||||||
};
|
};
|
||||||
|
static_assert(sizeof(ThreadContext) == 0x158);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
#if !defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
#error "Unknown Architecture for ams::svc::ThreadContext"
|
#error "Unknown Architecture for ams::svc::ThreadContext"
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum ThreadSuspend : u32 {
|
enum ThreadSuspend : u32 {
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ namespace ams::util {
|
|||||||
private:
|
private:
|
||||||
constexpr explicit ALWAYS_INLINE Iterator(ImplIterator it) : m_impl(it) { /* ... */ }
|
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 {
|
constexpr ALWAYS_INLINE ImplIterator GetImplIterator() const {
|
||||||
return m_impl;
|
return m_impl;
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ namespace ams::util {
|
|||||||
template<typename T, typename Derived>
|
template<typename T, typename Derived>
|
||||||
class OptionalBaseImpl {
|
class OptionalBaseImpl {
|
||||||
protected:
|
protected:
|
||||||
using StoredType = std::remove_const<T>::type;
|
using StoredType = std::remove_const_t<T>;
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
constexpr void ConstructImpl(Args &&... args) { static_cast<Derived *>(this)->m_payload.Construct(std::forward<Args>(args)...); }
|
constexpr void ConstructImpl(Args &&... args) { static_cast<Derived *>(this)->m_payload.Construct(std::forward<Args>(args)...); }
|
||||||
|
|||||||
@@ -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); }
|
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 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 {
|
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); }
|
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 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 {
|
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(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); }
|
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_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 {
|
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(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); }
|
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_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 {
|
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); }
|
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_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 {
|
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); }
|
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 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; }
|
constexpr friend bool operator==(const basic_string_view &lhs, const basic_string_view &rhs) noexcept { return lhs.compare(rhs) == 0; }
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ namespace ams::crypto::impl {
|
|||||||
|
|
||||||
/* Copy in any leftover data. */
|
/* Copy in any leftover data. */
|
||||||
if (const auto left = size % BlockSize; left > 0) {
|
if (const auto left = size % BlockSize; left > 0) {
|
||||||
std::memcpy(m_y, cur_block, size);
|
std::memcpy(m_y, cur_block, left);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,10 +49,9 @@ namespace ams::kern::init {
|
|||||||
constexpr PageTableEntry KernelRwDataUncachedAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemoryNotCacheable, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped);
|
constexpr PageTableEntry KernelRwDataUncachedAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemoryNotCacheable, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped);
|
||||||
|
|
||||||
void StoreDataCache(const void *addr, size_t size) {
|
void StoreDataCache(const void *addr, size_t size) {
|
||||||
uintptr_t start = util::AlignDown(reinterpret_cast<uintptr_t>(addr), cpu::DataCacheLineSize);
|
const uintptr_t start = util::AlignDown(reinterpret_cast<uintptr_t>(addr), cpu::DataCacheLineSize);
|
||||||
uintptr_t end = reinterpret_cast<uintptr_t>(addr) + size;
|
for (size_t stored = 0; stored < size; stored += cpu::DataCacheLineSize) {
|
||||||
for (uintptr_t cur = start; cur < end; cur += cpu::DataCacheLineSize) {
|
__asm__ __volatile__("dc cvac, %[cur]" :: [cur]"r"(start + stored) : "memory");
|
||||||
__asm__ __volatile__("dc cvac, %[cur]" :: [cur]"r"(cur) : "memory");
|
|
||||||
}
|
}
|
||||||
cpu::DataSynchronizationBarrier();
|
cpu::DataSynchronizationBarrier();
|
||||||
}
|
}
|
||||||
@@ -594,11 +593,13 @@ namespace ams::kern::init {
|
|||||||
|
|
||||||
switch (num_watchpoints) {
|
switch (num_watchpoints) {
|
||||||
FOR_I_IN_15_TO_1(MESOSPHERE_INITIALIZE_WATCHPOINT_CASE, 0)
|
FOR_I_IN_15_TO_1(MESOSPHERE_INITIALIZE_WATCHPOINT_CASE, 0)
|
||||||
|
case 0:
|
||||||
|
cpu::SetDbgWcr0El1(0);
|
||||||
|
cpu::SetDbgWvr0El1(0);
|
||||||
|
[[fallthrough]];
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cpu::SetDbgWcr0El1(0);
|
|
||||||
cpu::SetDbgWvr0El1(0);
|
|
||||||
|
|
||||||
switch (num_breakpoints) {
|
switch (num_breakpoints) {
|
||||||
FOR_I_IN_15_TO_1(MESOSPHERE_INITIALIZE_BREAKPOINT_CASE, 0)
|
FOR_I_IN_15_TO_1(MESOSPHERE_INITIALIZE_BREAKPOINT_CASE, 0)
|
||||||
|
|||||||
@@ -376,7 +376,7 @@ _ZN3ams4kern4arch5arm6430EL1SynchronousExceptionHandlerEv:
|
|||||||
stp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
|
stp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
|
||||||
stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
|
stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
|
||||||
|
|
||||||
mrs x20, sp_el0
|
ldr x20, [sp]
|
||||||
mrs x21, elr_el1
|
mrs x21, elr_el1
|
||||||
mrs x22, spsr_el1
|
mrs x22, spsr_el1
|
||||||
mrs x23, tpidr_el0
|
mrs x23, tpidr_el0
|
||||||
@@ -529,7 +529,7 @@ _ZN3ams4kern4arch5arm6421EL1SystemErrorHandlerEv:
|
|||||||
stp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
|
stp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
|
||||||
stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
|
stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
|
||||||
|
|
||||||
mrs x20, sp_el0
|
ldr x20, [sp]
|
||||||
mrs x21, elr_el1
|
mrs x21, elr_el1
|
||||||
mrs x22, spsr_el1
|
mrs x22, spsr_el1
|
||||||
mrs x23, tpidr_el0
|
mrs x23, tpidr_el0
|
||||||
|
|||||||
@@ -183,9 +183,7 @@ _ZN3ams4kern10KScheduler12ScheduleImplEv:
|
|||||||
|
|
||||||
/* Get the highest priority thread's context, and save it. */
|
/* Get the highest priority thread's context, and save it. */
|
||||||
/* ams::kern::KThread::GetContextForSchedulerLoop() */
|
/* ams::kern::KThread::GetContextForSchedulerLoop() */
|
||||||
mov x0, x21
|
add x22, x21, #(THREAD_THREAD_CONTEXT)
|
||||||
bl _ZN3ams4kern7KThread26GetContextForSchedulerLoopEv
|
|
||||||
mov x22, x0
|
|
||||||
|
|
||||||
/* Prepare to try to acquire the context lock. */
|
/* Prepare to try to acquire the context lock. */
|
||||||
add x1, x22, #(THREAD_CONTEXT_LOCKED)
|
add x1, x22, #(THREAD_CONTEXT_LOCKED)
|
||||||
@@ -227,26 +225,7 @@ _ZN3ams4kern10KScheduler12ScheduleImplEv:
|
|||||||
mov x0, x22
|
mov x0, x22
|
||||||
RESTORE_THREAD_CONTEXT(x0, x1, x2, 9f)
|
RESTORE_THREAD_CONTEXT(x0, x1, x2, 9f)
|
||||||
|
|
||||||
9: /* Configure single-step, if we should. */
|
9: /* We're done restoring the thread context, and can return safely. */
|
||||||
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
|
|
||||||
|
|
||||||
/* Get a reference to the new thread's stack parameters. */
|
|
||||||
add x2, sp, #0x1000
|
|
||||||
and x2, x2, #~(0x1000-1)
|
|
||||||
|
|
||||||
/* Read the single-step flag. */
|
|
||||||
ldurb w2, [x2, #-(THREAD_STACK_PARAMETERS_SIZE - THREAD_STACK_PARAMETERS_IS_SINGLE_STEP)]
|
|
||||||
|
|
||||||
/* Update the single-step bit in mdscr_el1. */
|
|
||||||
mrs x1, mdscr_el1
|
|
||||||
bic x1, x1, #1
|
|
||||||
orr x1, x1, x2
|
|
||||||
msr mdscr_el1, x1
|
|
||||||
|
|
||||||
isb
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* We're done restoring the thread context, and can return safely. */
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
10: /* Our switch failed. */
|
10: /* Our switch failed. */
|
||||||
|
|||||||
@@ -33,44 +33,6 @@ namespace ams::mitm::fs {
|
|||||||
constexpr const char AtmosphereHblWebContentDir[] = "/atmosphere/hbl_html/";
|
constexpr const char AtmosphereHblWebContentDir[] = "/atmosphere/hbl_html/";
|
||||||
constexpr const char ProgramWebContentDir[] = "/manual_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 os::SdkMutex g_boot0_detect_lock;
|
||||||
constinit bool g_detected_boot0_kind = false;
|
constinit bool g_detected_boot0_kind = false;
|
||||||
constinit bool g_is_boot0_custom_public_key = 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;
|
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) {
|
bool GetSettingsItemBooleanValue(const char *name, const char *key) {
|
||||||
u8 tmp = 0;
|
u8 tmp = 0;
|
||||||
AMS_ABORT_UNLESS(settings::fwdbg::GetSettingsItemValue(std::addressof(tmp), sizeof(tmp), name, key) == sizeof(tmp));
|
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)));
|
R_TRY(fsOpenDataStorageByCurrentProcessFwd(m_forward_service.get(), std::addressof(data_storage)));
|
||||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
||||||
|
|
||||||
/* Get a scoped lock. */
|
/* Get a layered storage for the process romfs. */
|
||||||
std::scoped_lock lk(g_data_storage_lock);
|
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(m_client_info.program_id, data_storage, true)), target_object_id);
|
||||||
|
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,43 +311,13 @@ namespace ams::mitm::fs {
|
|||||||
/* Only mitm if there is actually an override romfs. */
|
/* Only mitm if there is actually an override romfs. */
|
||||||
R_UNLESS(mitm::fs::HasSdRomfsContent(data_id), sm::mitm::ResultShouldForwardToSession());
|
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;
|
FsStorage data_storage;
|
||||||
R_TRY(fsOpenDataStorageByDataIdFwd(m_forward_service.get(), std::addressof(data_storage), static_cast<u64>(data_id), static_cast<NcmStorageId>(storage_id)));
|
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))};
|
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
||||||
|
|
||||||
/* Get a scoped lock. */
|
/* Get a layered storage for the data id. */
|
||||||
std::scoped_lock lk(g_data_storage_lock);
|
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(data_id, data_storage, false)), target_object_id);
|
||||||
|
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,38 +337,8 @@ namespace ams::mitm::fs {
|
|||||||
R_TRY(fsOpenDataStorageWithProgramIndexFwd(m_forward_service.get(), std::addressof(data_storage), program_index));
|
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))};
|
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
||||||
|
|
||||||
/* Get a scoped lock. */
|
/* Get a layered storage for the process romfs. */
|
||||||
std::scoped_lock lk(g_data_storage_lock);
|
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(program_id, data_storage, true)), target_object_id);
|
||||||
|
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,19 +28,121 @@ namespace ams::mitm::fs {
|
|||||||
os::MessageQueue g_req_mq(g_mq_storage + 0, 1);
|
os::MessageQueue g_req_mq(g_mq_storage + 0, 1);
|
||||||
os::MessageQueue g_ack_mq(g_mq_storage + 1, 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 *) {
|
void RomfsInitializerThreadFunction(void *) {
|
||||||
while (true) {
|
while (true) {
|
||||||
uintptr_t storage_uptr = 0;
|
uintptr_t storage_uptr = 0;
|
||||||
|
|
||||||
g_req_mq.Receive(std::addressof(storage_uptr));
|
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);
|
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;
|
constexpr size_t RomfsInitializerThreadStackSize = 0x8000;
|
||||||
os::ThreadType g_romfs_initializer_thread;
|
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_initializer_thread_stack[RomfsInitializerThreadStackSize];
|
||||||
|
alignas(os::ThreadStackAlignment) u8 g_romfs_finalizer_thread_stack[os::MemoryPageSize];
|
||||||
|
|
||||||
void RequestInitializeStorage(uintptr_t storage_uptr) {
|
void RequestInitializeStorage(uintptr_t storage_uptr) {
|
||||||
std::scoped_lock lk(g_mq_lock);
|
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)));
|
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::SetThreadNamePointer(std::addressof(g_romfs_initializer_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm_fs, RomFileSystemInitializeThread));
|
||||||
os::StartThread(std::addressof(g_romfs_initializer_thread));
|
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;
|
g_started_req_thread = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,27 +165,113 @@ namespace ams::mitm::fs {
|
|||||||
AMS_ABORT_UNLESS(ack == storage_uptr);
|
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;
|
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++) {
|
for (size_t i = 0; i < m_source_infos.size(); i++) {
|
||||||
m_source_infos[i].Cleanup();
|
m_source_infos[i].Cleanup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LayeredRomfsStorage::BeginInitialize() {
|
void LayeredRomfsStorageImpl::BeginInitialize() {
|
||||||
AMS_ABORT_UNLESS(!m_started_initialize);
|
AMS_ABORT_UNLESS(!m_started_initialize);
|
||||||
RequestInitializeStorage(reinterpret_cast<uintptr_t>(this));
|
RequestInitializeStorage(reinterpret_cast<uintptr_t>(this));
|
||||||
m_started_initialize = true;
|
m_started_initialize = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LayeredRomfsStorage::InitializeImpl() {
|
void LayeredRomfsStorageImpl::InitializeImpl() {
|
||||||
/* Build new virtual romfs. */
|
/* Build new virtual romfs. */
|
||||||
romfs::Builder builder(m_program_id);
|
romfs::Builder builder(m_program_id);
|
||||||
|
|
||||||
@@ -98,7 +291,7 @@ namespace ams::mitm::fs {
|
|||||||
m_initialize_event.Signal();
|
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. */
|
/* Check if we can succeed immediately. */
|
||||||
R_SUCCEED_IF(size == 0);
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
@@ -178,7 +371,7 @@ namespace ams::mitm::fs {
|
|||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result LayeredRomfsStorage::GetSize(s64 *out_size) {
|
Result LayeredRomfsStorageImpl::GetSize(s64 *out_size) {
|
||||||
/* Ensure we're initialized. */
|
/* Ensure we're initialized. */
|
||||||
if (!m_is_initialized) {
|
if (!m_is_initialized) {
|
||||||
m_initialize_event.Wait();
|
m_initialize_event.Wait();
|
||||||
@@ -188,11 +381,11 @@ namespace ams::mitm::fs {
|
|||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result LayeredRomfsStorage::Flush() {
|
Result LayeredRomfsStorageImpl::Flush() {
|
||||||
return ResultSuccess();
|
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);
|
AMS_UNUSED(offset, src, src_size);
|
||||||
|
|
||||||
switch (op_id) {
|
switch (op_id) {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
namespace ams::mitm::fs {
|
namespace ams::mitm::fs {
|
||||||
|
|
||||||
class LayeredRomfsStorage : public std::enable_shared_from_this<LayeredRomfsStorage>, public ams::fs::IStorage {
|
class LayeredRomfsStorageImpl {
|
||||||
private:
|
private:
|
||||||
std::vector<romfs::SourceInfo> m_source_infos;
|
std::vector<romfs::SourceInfo> m_source_infos;
|
||||||
std::unique_ptr<ams::fs::IStorage> m_storage_romfs;
|
std::unique_ptr<ams::fs::IStorage> m_storage_romfs;
|
||||||
@@ -35,32 +35,20 @@ namespace ams::mitm::fs {
|
|||||||
return back.virtual_offset + back.size;
|
return back.virtual_offset + back.size;
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
LayeredRomfsStorage(std::unique_ptr<ams::fs::IStorage> s_r, std::unique_ptr<ams::fs::IStorage> f_r, ncm::ProgramId pr_id);
|
LayeredRomfsStorageImpl(std::unique_ptr<ams::fs::IStorage> s_r, std::unique_ptr<ams::fs::IStorage> f_r, ncm::ProgramId pr_id);
|
||||||
virtual ~LayeredRomfsStorage();
|
~LayeredRomfsStorageImpl();
|
||||||
|
|
||||||
void BeginInitialize();
|
void BeginInitialize();
|
||||||
void InitializeImpl();
|
void InitializeImpl();
|
||||||
|
|
||||||
std::shared_ptr<LayeredRomfsStorage> GetShared() {
|
constexpr ncm::ProgramId GetProgramId() const { return m_program_id; }
|
||||||
return this->shared_from_this();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
Result Read(s64 offset, void *buffer, size_t size);
|
||||||
virtual Result GetSize(s64 *out_size) override;
|
Result GetSize(s64 *out_size);
|
||||||
virtual Result Flush() override;
|
Result 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;
|
Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t 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();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<ams::fs::IStorage> GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,28 +64,61 @@ namespace ams::mitm::fs {
|
|||||||
};
|
};
|
||||||
static_assert(util::is_pod<FileEntry>::value && sizeof(FileEntry) == 0x20);
|
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>
|
template<typename Entry>
|
||||||
class TableReader {
|
class TableReader : public DynamicTableCache {
|
||||||
NON_COPYABLE(TableReader);
|
NON_COPYABLE(TableReader);
|
||||||
NON_MOVEABLE(TableReader);
|
NON_MOVEABLE(TableReader);
|
||||||
private:
|
private:
|
||||||
static constexpr size_t MaxCachedSize = (1_MB / 4);
|
|
||||||
static constexpr size_t FallbackCacheSize = 1_KB;
|
static constexpr size_t FallbackCacheSize = 1_KB;
|
||||||
private:
|
private:
|
||||||
ams::fs::IStorage *m_storage;
|
ams::fs::IStorage *m_storage;
|
||||||
size_t m_offset;
|
size_t m_offset;
|
||||||
size_t m_size;
|
size_t m_size;
|
||||||
size_t m_cache_idx;
|
size_t m_cache_idx;
|
||||||
void *m_cache;
|
|
||||||
u8 m_fallback_cache[FallbackCacheSize];
|
u8 m_fallback_cache[FallbackCacheSize];
|
||||||
private:
|
private:
|
||||||
ALWAYS_INLINE void Read(size_t ofs, void *dst, size_t size) {
|
ALWAYS_INLINE void Read(size_t ofs, void *dst, size_t size) {
|
||||||
R_ABORT_UNLESS(m_storage->Read(m_offset + ofs, dst, size));
|
R_ABORT_UNLESS(m_storage->Read(m_offset + ofs, dst, size));
|
||||||
}
|
}
|
||||||
ALWAYS_INLINE void ReloadCacheImpl(size_t idx) {
|
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);
|
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);
|
this->Read(rel_ofs, m_cache, new_cache_size);
|
||||||
m_cache_idx = idx;
|
m_cache_idx = idx;
|
||||||
}
|
}
|
||||||
@@ -97,23 +130,18 @@ namespace ams::mitm::fs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE size_t GetCacheIndex(u32 ofs) {
|
ALWAYS_INLINE size_t GetCacheIndex(u32 ofs) {
|
||||||
return ofs / MaxCachedSize;
|
return ofs / this->GetCacheSize();
|
||||||
}
|
}
|
||||||
public:
|
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) {
|
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) {
|
||||||
m_cache = std::malloc(std::min(sz, MaxCachedSize));
|
|
||||||
AMS_ABORT_UNLESS(m_cache != nullptr);
|
AMS_ABORT_UNLESS(m_cache != nullptr);
|
||||||
this->ReloadCacheImpl(0);
|
this->ReloadCacheImpl(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
~TableReader() {
|
|
||||||
std::free(m_cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Entry *GetEntry(u32 entry_offset) {
|
const Entry *GetEntry(u32 entry_offset) {
|
||||||
this->ReloadCache(this->GetCacheIndex(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);
|
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)))) {
|
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>
|
template<typename Entry>
|
||||||
class TableWriter {
|
class TableWriter : public DynamicTableCache {
|
||||||
NON_COPYABLE(TableWriter);
|
NON_COPYABLE(TableWriter);
|
||||||
NON_MOVEABLE(TableWriter);
|
NON_MOVEABLE(TableWriter);
|
||||||
private:
|
private:
|
||||||
static constexpr size_t MaxCachedSize = (1_MB / 4);
|
|
||||||
static constexpr size_t FallbackCacheSize = 1_KB;
|
static constexpr size_t FallbackCacheSize = 1_KB;
|
||||||
private:
|
private:
|
||||||
::FsFile *m_file;
|
::FsFile *m_file;
|
||||||
size_t m_offset;
|
size_t m_offset;
|
||||||
size_t m_size;
|
size_t m_size;
|
||||||
size_t m_cache_idx;
|
size_t m_cache_idx;
|
||||||
void *m_cache;
|
|
||||||
u8 m_fallback_cache[FallbackCacheSize];
|
u8 m_fallback_cache[FallbackCacheSize];
|
||||||
size_t m_fallback_cache_entry_offset;
|
size_t m_fallback_cache_entry_offset;
|
||||||
size_t m_fallback_cache_entry_size;
|
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));
|
AMS_ABORT_UNLESS(!(m_cache_dirty && m_fallback_cache_dirty));
|
||||||
|
|
||||||
if (m_cache_dirty) {
|
if (m_cache_dirty) {
|
||||||
const size_t ofs = m_cache_idx * MaxCachedSize;
|
const size_t ofs = m_cache_idx * this->GetCacheSize();
|
||||||
this->Write(ofs, m_cache, std::min(m_size - ofs, MaxCachedSize));
|
this->Write(ofs, m_cache, std::min(m_size - ofs, this->GetCacheSize()));
|
||||||
m_cache_dirty = false;
|
m_cache_dirty = false;
|
||||||
}
|
}
|
||||||
if (m_fallback_cache_dirty) {
|
if (m_fallback_cache_dirty) {
|
||||||
@@ -168,12 +194,12 @@ namespace ams::mitm::fs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE size_t GetCacheIndex(u32 ofs) {
|
ALWAYS_INLINE size_t GetCacheIndex(u32 ofs) {
|
||||||
return ofs / MaxCachedSize;
|
return ofs / this->GetCacheSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE void RefreshCacheImpl() {
|
ALWAYS_INLINE void RefreshCacheImpl() {
|
||||||
const size_t cur_cache = m_cache_idx * MaxCachedSize;
|
const size_t cur_cache = m_cache_idx * this->GetCacheSize();
|
||||||
this->Read(cur_cache, m_cache, std::min(m_size - cur_cache, MaxCachedSize));
|
this->Read(cur_cache, m_cache, std::min(m_size - cur_cache, this->GetCacheSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE void RefreshCache(u32 entry_offset) {
|
ALWAYS_INLINE void RefreshCache(u32 entry_offset) {
|
||||||
@@ -184,29 +210,27 @@ namespace ams::mitm::fs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public:
|
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() {
|
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() {
|
||||||
const size_t cache_size = std::min(sz, MaxCachedSize);
|
|
||||||
m_cache = std::malloc(cache_size);
|
|
||||||
AMS_ABORT_UNLESS(m_cache != nullptr);
|
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));
|
std::memset(m_fallback_cache, 0, sizeof(m_fallback_cache));
|
||||||
for (size_t cur = 0; cur < 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, MaxCachedSize));
|
this->Write(cur, m_cache, std::min(m_size - cur, this->GetCacheSize()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~TableWriter() {
|
~TableWriter() {
|
||||||
this->Flush();
|
this->Flush();
|
||||||
std::free(m_cache);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry *GetEntry(u32 entry_offset, u32 name_len) {
|
Entry *GetEntry(u32 entry_offset, u32 name_len) {
|
||||||
this->RefreshCache(entry_offset);
|
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);
|
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();
|
this->Flush();
|
||||||
|
|
||||||
m_fallback_cache_entry_offset = entry_offset;
|
m_fallback_cache_entry_offset = entry_offset;
|
||||||
@@ -226,7 +250,6 @@ namespace ams::mitm::fs {
|
|||||||
using DirectoryTableWriter = TableWriter<DirectoryEntry>;
|
using DirectoryTableWriter = TableWriter<DirectoryEntry>;
|
||||||
using FileTableWriter = TableWriter<FileEntry>;
|
using FileTableWriter = TableWriter<FileEntry>;
|
||||||
|
|
||||||
|
|
||||||
constexpr inline u32 CalculatePathHash(u32 parent, const char *_path, u32 start, size_t path_len) {
|
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);
|
const unsigned char *path = reinterpret_cast<const unsigned char *>(_path);
|
||||||
u32 hash = parent ^ 123456789;
|
u32 hash = parent ^ 123456789;
|
||||||
@@ -526,113 +549,151 @@ namespace ams::mitm::fs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Populate file tables. */
|
/* Set all files' hash value = hash index. */
|
||||||
{
|
for (const auto &it : m_files) {
|
||||||
/* Allocate the hash table. */
|
BuildFileContext *cur_file = it.get();
|
||||||
void *fht_buf = std::malloc(m_file_hash_table_size);
|
cur_file->hash_value = CalculatePathHash(cur_file->parent->entry_offset, cur_file->path.get(), 0, cur_file->path_len) % num_file_hash_table_entries;
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Write the file table. */
|
/* Set all directories' hash value = hash index. */
|
||||||
{
|
for (const auto &it : m_directories) {
|
||||||
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);
|
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) {
|
for (const auto &it : m_files) {
|
||||||
BuildFileContext *cur_file = it.get();
|
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. */
|
if (const auto hash_ind = cur_file->hash_value; ofs_ind <= hash_ind && hash_ind < end_ind) {
|
||||||
cur_entry->parent = cur_file->parent->entry_offset;
|
cur_file->hash_value = hash_table[hash_ind - ofs_ind];
|
||||||
cur_entry->sibling = (cur_file->sibling == nullptr) ? EmptyEntry : cur_file->sibling->entry_offset;
|
hash_table[hash_ind - ofs_ind] = cur_file->entry_offset;
|
||||||
cur_entry->offset = cur_file->offset;
|
|
||||||
cur_entry->size = cur_file->size;
|
|
||||||
|
|
||||||
/* Insert into hash table. */
|
cur_file->SetHashMark();
|
||||||
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;
|
|
||||||
|
|
||||||
/* Set name. */
|
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));
|
||||||
cur_entry->name_size = name_size;
|
}
|
||||||
if (name_size) {
|
|
||||||
std::memcpy(cur_entry->name, cur_file->path.get(), name_size);
|
/* Write the directory hash table. */
|
||||||
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
|
for (size_t ofs = 0; ofs < m_dir_hash_table_size; ofs += hash_table_size) {
|
||||||
cur_entry->name[i] = 0;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
case DataSourceType::LooseSdFile:
|
||||||
/* Emplace a source. */
|
{
|
||||||
switch (cur_file->source_type) {
|
char *new_path = new char[cur_file->GetPathLength() + 1];
|
||||||
case DataSourceType::Storage:
|
cur_file->GetPath(new_path);
|
||||||
case DataSourceType::File:
|
out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, new_path);
|
||||||
{
|
}
|
||||||
/* Try to compact if possible. */
|
break;
|
||||||
auto &back = out_infos->back();
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Populate directory tables. */
|
/* Write the directory table. */
|
||||||
{
|
{
|
||||||
/* Allocate the hash table. */
|
DirectoryTableWriter dir_table(std::addressof(metadata_file), m_dir_hash_table_size, m_dir_table_size);
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Write the file table. */
|
for (const auto &it : m_directories) {
|
||||||
{
|
BuildDirectoryContext *cur_dir = it.get();
|
||||||
DirectoryTableWriter dir_table(std::addressof(metadata_file), m_dir_hash_table_size, m_dir_table_size);
|
DirectoryEntry *cur_entry = dir_table.GetEntry(cur_dir->entry_offset, cur_dir->path_len);
|
||||||
|
|
||||||
for (const auto &it : m_directories) {
|
cur_dir->ClearHashMark();
|
||||||
BuildDirectoryContext *cur_dir = it.get();
|
|
||||||
DirectoryEntry *cur_entry = dir_table.GetEntry(cur_dir->entry_offset, cur_dir->path_len);
|
|
||||||
|
|
||||||
/* Set entry fields. */
|
/* Set entry fields. */
|
||||||
cur_entry->parent = cur_dir == m_root ? 0 : cur_dir->parent->entry_offset;
|
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->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->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->file = (cur_dir->file == nullptr) ? EmptyEntry : cur_dir->file->entry_offset;
|
||||||
|
cur_entry->hash = cur_dir->hash_value;
|
||||||
|
|
||||||
/* Insert into hash table. */
|
/* Set name. */
|
||||||
const u32 name_size = cur_dir->path_len;
|
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->name_size = name_size;
|
||||||
cur_entry->hash = dir_hash_table[hash_ind];
|
if (name_size) {
|
||||||
dir_hash_table[hash_ind] = cur_dir->entry_offset;
|
std::memcpy(cur_entry->name, cur_dir->path.get(), name_size);
|
||||||
|
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
|
||||||
/* Set name. */
|
cur_entry->name[i] = 0;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
#include "../dns_mitm/dnsmitm_debug.hpp"
|
||||||
|
|
||||||
namespace ams::mitm::fs::romfs {
|
namespace ams::mitm::fs::romfs {
|
||||||
|
|
||||||
enum class DataSourceType {
|
enum class DataSourceType : u8 {
|
||||||
Storage,
|
Storage,
|
||||||
File,
|
File,
|
||||||
LooseSdFile,
|
LooseSdFile,
|
||||||
@@ -120,10 +121,11 @@ namespace ams::mitm::fs::romfs {
|
|||||||
BuildFileContext *file;
|
BuildFileContext *file;
|
||||||
u32 path_len;
|
u32 path_len;
|
||||||
u32 entry_offset;
|
u32 entry_offset;
|
||||||
|
u32 hash_value;
|
||||||
|
|
||||||
struct RootTag{};
|
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);
|
this->path = std::make_unique<char[]>(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +156,18 @@ namespace ams::mitm::fs::romfs {
|
|||||||
dst[parent_len + 1 + this->path_len] = '\x00';
|
dst[parent_len + 1 + this->path_len] = '\x00';
|
||||||
return parent_len + 1 + this->path_len;
|
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 {
|
struct BuildFileContext {
|
||||||
@@ -168,9 +182,10 @@ namespace ams::mitm::fs::romfs {
|
|||||||
s64 orig_offset;
|
s64 orig_offset;
|
||||||
u32 path_len;
|
u32 path_len;
|
||||||
u32 entry_offset;
|
u32 entry_offset;
|
||||||
|
u32 hash_value;
|
||||||
DataSourceType source_type;
|
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_len = entry_name_len;
|
||||||
this->path = std::unique_ptr<char[]>(new char[this->path_len + 1]);
|
this->path = std::unique_ptr<char[]>(new char[this->path_len + 1]);
|
||||||
std::memcpy(this->path.get(), entry_name, entry_name_len);
|
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';
|
dst[parent_len + 1 + this->path_len] = '\x00';
|
||||||
return parent_len + 1 + this->path_len;
|
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;
|
class DirectoryTableReader;
|
||||||
|
|||||||
@@ -378,6 +378,12 @@ namespace ams::settings::fwdbg {
|
|||||||
/* 0 = Disabled, 1 = Enabled */
|
/* 0 = Disabled, 1 = Enabled */
|
||||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_htc", "u8!0x0"));
|
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. */
|
/* Controls whether atmosphere's log manager is enabled. */
|
||||||
/* Note that this setting is ignored (and treated as 1) when htc is enabled. */
|
/* Note that this setting is ignored (and treated as 1) when htc is enabled. */
|
||||||
/* 0 = Disabled, 1 = Enabled */
|
/* 0 = Disabled, 1 = Enabled */
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ namespace ams::mitm::sysupdater {
|
|||||||
ON_SCOPE_EXIT { std::free(data_buffer); };
|
ON_SCOPE_EXIT { std::free(data_buffer); };
|
||||||
|
|
||||||
/* Declare helper for result validation. */
|
/* Declare helper for result validation. */
|
||||||
auto ValidateResult = [&] ALWAYS_INLINE_LAMBDA (Result result) -> Result {
|
auto ValidateResult = [&](Result result) ALWAYS_INLINE_LAMBDA -> Result {
|
||||||
*out_result = result;
|
*out_result = result;
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -101,11 +101,9 @@ namespace ams::creport {
|
|||||||
m_has_extra_info = has_extra_info;
|
m_has_extra_info = has_extra_info;
|
||||||
|
|
||||||
if (this->OpenProcess(process_id)) {
|
if (this->OpenProcess(process_id)) {
|
||||||
ON_SCOPE_EXIT { this->Close(); };
|
|
||||||
|
|
||||||
/* Parse info from the crashed process. */
|
/* Parse info from the crashed process. */
|
||||||
this->ProcessExceptions();
|
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());
|
m_thread_list->ReadFromProcess(m_debug_handle, m_thread_tls_map, this->Is64Bit());
|
||||||
|
|
||||||
/* Associate module list to threads. */
|
/* 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, */
|
/* 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. */
|
/* but there's no reason for us not to always go looking. */
|
||||||
for (size_t i = 0; i < m_thread_list->GetThreadCount(); i++) {
|
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. */
|
/* 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. */
|
/* Finalize our heap. */
|
||||||
std::destroy_at(m_module_list);
|
std::destroy_at(m_module_list);
|
||||||
std::destroy_at(m_thread_list);
|
std::destroy_at(m_thread_list);
|
||||||
@@ -358,7 +361,7 @@ namespace ams::creport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CrashReport::SaveToFile(ScopedFile &file) {
|
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());
|
file.WriteFormat("Result: 0x%X (2%03d-%04d)\n\n", m_result.GetValue(), m_result.GetModule(), m_result.GetDescription());
|
||||||
|
|
||||||
|
|||||||
@@ -103,13 +103,17 @@ namespace ams {
|
|||||||
g_crash_report.Initialize();
|
g_crash_report.Initialize();
|
||||||
|
|
||||||
/* Try to debug the crashed process. */
|
/* Try to debug the crashed process. */
|
||||||
g_crash_report.BuildReport(crashed_pid, has_extra_info);
|
{
|
||||||
if (!g_crash_report.IsComplete()) {
|
g_crash_report.BuildReport(crashed_pid, has_extra_info);
|
||||||
return;
|
ON_SCOPE_EXIT { if (g_crash_report.IsOpen()) { g_crash_report.Close(); } };
|
||||||
}
|
|
||||||
|
|
||||||
/* Save report to file. */
|
if (!g_crash_report.IsComplete()) {
|
||||||
g_crash_report.SaveReport(enable_screenshot);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save report to file. */
|
||||||
|
g_crash_report.SaveReport(enable_screenshot);
|
||||||
|
}
|
||||||
|
|
||||||
/* Try to terminate the process, if we should. */
|
/* Try to terminate the process, if we should. */
|
||||||
const auto fw_ver = hos::GetVersion();
|
const auto fw_ver = hos::GetVersion();
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace ams::creport {
|
|||||||
|
|
||||||
struct ModulePath {
|
struct ModulePath {
|
||||||
u32 zero;
|
u32 zero;
|
||||||
u32 path_length;
|
s32 path_length;
|
||||||
char path[ModulePathLengthMax];
|
char path[ModulePathLengthMax];
|
||||||
};
|
};
|
||||||
static_assert(sizeof(ModulePath) == 0x208, "ModulePath definition!");
|
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. */
|
/* Set the debug handle, for access in other member functions. */
|
||||||
m_debug_handle = debug_handle;
|
m_debug_handle = debug_handle;
|
||||||
|
|
||||||
/* Try to add the thread's PC. */
|
/* 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. */
|
/* 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. */
|
/* Try to add all the addresses in the thread's stacktrace. */
|
||||||
for (size_t i = 0; i < thread.GetStackTraceSize(); i++) {
|
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. */
|
/* Try to locate module from guess. */
|
||||||
uintptr_t base_address = 0;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,9 +105,18 @@ namespace ams::creport {
|
|||||||
module.end_address = mi.base_address + mi.size;
|
module.end_address = mi.base_address + mi.size;
|
||||||
GetModuleName(module.name, module.start_address, module.end_address);
|
GetModuleName(module.name, module.start_address, module.end_address);
|
||||||
GetModuleId(module.module_id, 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) {
|
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]);
|
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. */
|
/* Query the memory region our guess falls in. */
|
||||||
svc::MemoryInfo mi;
|
svc::MemoryInfo mi;
|
||||||
svc::PageInfo pi;
|
svc::PageInfo pi;
|
||||||
@@ -199,7 +210,7 @@ namespace ams::creport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Also validate that we're looking at a valid name. */
|
/* 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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,7 +219,7 @@ namespace ams::creport {
|
|||||||
/* Start after last slash in path. */
|
/* Start after last slash in path. */
|
||||||
const char *path = rodata_start.module_path.path;
|
const char *path = rodata_start.module_path.path;
|
||||||
int ofs;
|
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] == '\\') {
|
if (path[ofs] == '/' || path[ofs] == '\\') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -216,8 +227,8 @@ namespace ams::creport {
|
|||||||
ofs++;
|
ofs++;
|
||||||
|
|
||||||
/* Copy name to output. */
|
/* Copy name to output. */
|
||||||
const size_t name_size = std::min(ModuleNameLengthMax, sizeof(rodata_start.module_path.path) - ofs);
|
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::strncpy(out_name, path + ofs, name_size);
|
std::memcpy(out_name, path + ofs, name_size);
|
||||||
out_name[ModuleNameLengthMax - 1] = '\x00';
|
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) {
|
const char *ModuleList::GetFormattedAddressString(uintptr_t address) {
|
||||||
/* Print default formatted string. */
|
/* Print default formatted string. */
|
||||||
util::SNPrintf(m_address_str_buf, sizeof(m_address_str_buf), "%016lx", address);
|
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++) {
|
for (size_t i = 0; i < m_num_modules; i++) {
|
||||||
const auto& module = m_modules[i];
|
const auto& module = m_modules[i];
|
||||||
if (module.start_address <= address && address < module.end_address) {
|
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);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ namespace ams::creport {
|
|||||||
u8 module_id[ModuleIdSize];
|
u8 module_id[ModuleIdSize];
|
||||||
u64 start_address;
|
u64 start_address;
|
||||||
u64 end_address;
|
u64 end_address;
|
||||||
|
bool has_sym_table;
|
||||||
|
u64 sym_tab;
|
||||||
|
u64 str_tab;
|
||||||
|
u32 num_sym;
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
os::NativeHandle m_debug_handle;
|
os::NativeHandle m_debug_handle;
|
||||||
@@ -37,7 +41,7 @@ namespace ams::creport {
|
|||||||
ModuleInfo m_modules[ModuleCountMax];
|
ModuleInfo m_modules[ModuleCountMax];
|
||||||
|
|
||||||
/* For pretty-printing. */
|
/* For pretty-printing. */
|
||||||
char m_address_str_buf[0x280];
|
char m_address_str_buf[1_KB];
|
||||||
public:
|
public:
|
||||||
ModuleList() : m_debug_handle(os::InvalidNativeHandle), m_num_modules(0) {
|
ModuleList() : m_debug_handle(os::InvalidNativeHandle), m_num_modules(0) {
|
||||||
std::memset(m_modules, 0, sizeof(m_modules));
|
std::memset(m_modules, 0, sizeof(m_modules));
|
||||||
@@ -51,14 +55,15 @@ namespace ams::creport {
|
|||||||
return m_modules[i].start_address;
|
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);
|
const char *GetFormattedAddressString(uintptr_t address);
|
||||||
void SaveToFile(ScopedFile &file);
|
void SaveToFile(ScopedFile &file);
|
||||||
private:
|
private:
|
||||||
bool TryFindModule(uintptr_t *out_address, uintptr_t guess);
|
bool TryFindModule(uintptr_t *out_address, uintptr_t guess, bool is_64_bit);
|
||||||
void TryAddModule(uintptr_t guess);
|
void TryAddModule(uintptr_t guess, bool is_64_bit);
|
||||||
void GetModuleName(char *out_name, uintptr_t text_start, uintptr_t ro_start);
|
void GetModuleName(char *out_name, uintptr_t text_start, uintptr_t ro_start);
|
||||||
void GetModuleId(u8 *out, 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
Reference in New Issue
Block a user