Compare commits

...

33 Commits
1.2.1 ... 1.2.3

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

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

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

Hard to imagine a worse torture-test for this code than animal crossing.
2021-11-03 23:52:38 -07:00
Michael Scire
a73e0e8f16 ams: add arm/armv8a to gitignore 2021-10-31 11:20:32 -07:00
Michael Scire
2161365f4f ams: update for awareness of architecture revision 2021-10-31 11:18:17 -07:00
Michael Scire
258a83684e creport: print symbols if they're present 2021-10-30 14:18:00 -07:00
Michael Scire
d2a757c39e git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "ceff2f371"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "ceff2f371"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-10-30 11:21:54 -07:00
Michael Scire
6cf5205a28 spl: fix legacy physical keyslot compatibility 2021-10-30 11:21:25 -07:00
Michael Scire
b9c90b9234 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "4d0f1b792"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "4d0f1b792"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-10-29 17:47:35 -07:00
Michael Scire
94e18b8c93 ams: bump version to 1.2.2 2021-10-29 17:47:01 -07:00
Michael Scire
4e92687cab sprofile: various correctness fixes. 2021-10-29 17:38:44 -07:00
Michael Scire
2a0b99d9f9 sprofile: fix off-by-one in struct definition, fix GetImportableProfileUrls 2021-10-29 15:41:25 -07:00
Michael Scire
d1f3c4904b kern: fix minor assembly bugs, avoid unnecessary function call in KScheduler hotloop 2021-10-28 19:16:23 -07:00
Michael Scire
92321ccbc8 kern: fix 32-bit light ipc svc handler asm
Nintendo used to do what we were doing because the function wasn't directly in the handler table,
but we've always been directly in the handler table, so we were trashing the last four arguments to light ipc
when called from aarch32. Nothing uses this, but needed to be fixed.
2021-10-28 15:42:52 -07:00
Michael Scire
db3004e844 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "0a0bd74ca"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "0a0bd74ca"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-10-27 22:57:39 -07:00
Michael Scire
3e97e4addf init: disable fsdev cwd to prevent abort on fsdev usage (fsdev should not be used) 2021-10-27 22:57:09 -07:00
Michael Scire
4b7b33809f kern: optimize and bring into line with N our pstate.i management 2021-10-27 15:00:07 -07:00
Michael Scire
e81a1ce5a8 kern: audit (and fix) our hardware maintenance instructions to match official kernel 2021-10-27 12:31:53 -07:00
86 changed files with 2064 additions and 818 deletions

4
.gitignore vendored
View File

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

View File

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

View File

@@ -1,4 +1,48 @@
# Changelog
## 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
+ Support was implemented for 13.1.0.
+ `mesosphère` was updated to reflect the kernel behavioral changes made in 13.1.0.

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master
commit = 13c6987cc43d0b9a874e87e3ce8eaffd1c72fa69
parent = cb38b0b929f332a52ceeee5c27cb0fd83ff03433
commit = e7b84767c6f3dd4c2a97fa00201342c237bf67b4
parent = 0bbc907907f909f67c1ba1ce89f8efb3b79b704a
method = merge
cmdver = 0.4.1

View File

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

View File

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

View File

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

View File

@@ -93,3 +93,4 @@
/* Deferred includes. */
#include <mesosphere/kern_k_auto_object_impls.hpp>
#include <mesosphere/kern_k_scheduler_impls.hpp>

View File

@@ -279,7 +279,7 @@ namespace ams::kern::arch::arm64::init {
/* Invalidate the entire tlb. */
cpu::DataSynchronizationBarrierInnerShareable();
cpu::InvalidateEntireTlb();
cpu::InvalidateEntireTlbInnerShareable();
/* Copy data, if we should. */
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 (!l1_entry->IsTable()) {
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
ClearNewPageTable(new_table);
*l1_entry = L1PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
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) {
for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) {
l2_entry[i] = L2PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, PageTableEntry::SoftwareReservedBit_None, true);
cpu::DataSynchronizationBarrierInnerShareable();
virt_addr += L2BlockSize;
phys_addr += L2BlockSize;
size -= L2BlockSize;
}
cpu::DataSynchronizationBarrierInnerShareable();
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 (!l2_entry->IsTable()) {
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
ClearNewPageTable(new_table);
*l2_entry = L2PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
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) {
for (size_t i = 0; i < L3ContiguousBlockSize / L3BlockSize; i++) {
l3_entry[i] = L3PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, PageTableEntry::SoftwareReservedBit_None, true);
cpu::DataSynchronizationBarrierInnerShareable();
virt_addr += L3BlockSize;
phys_addr += L3BlockSize;
size -= L3BlockSize;
}
cpu::DataSynchronizationBarrierInnerShareable();
continue;
}

View File

@@ -19,6 +19,9 @@
/* TODO: Different header for this? */
#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 */
#define THREAD_STACK_PARAMETERS_SIZE 0x30
#define THREAD_STACK_PARAMETERS_SVC_PERMISSION 0x00

View File

@@ -60,6 +60,11 @@ namespace ams::kern::arch::arm64::cpu {
__asm__ __volatile__("isb" ::: "memory");
}
ALWAYS_INLINE void EnsureInstructionConsistencyInnerShareable() {
DataSynchronizationBarrierInnerShareable();
InstructionMemoryBarrier();
}
ALWAYS_INLINE void EnsureInstructionConsistency() {
DataSynchronizationBarrier();
InstructionMemoryBarrier();
@@ -177,7 +182,6 @@ namespace ams::kern::arch::arm64::cpu {
NOINLINE void SynchronizeAllCores();
/* Cache management helpers. */
void ClearPageToZeroImpl(void *);
void StoreEntireCacheForInit();
void FlushEntireCacheForInit();
@@ -190,10 +194,16 @@ namespace ams::kern::arch::arm64::cpu {
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(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) {
@@ -213,6 +223,11 @@ namespace ams::kern::arch::arm64::cpu {
EnsureInstructionConsistency();
}
ALWAYS_INLINE void InvalidateEntireTlbInnerShareable() {
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
EnsureInstructionConsistencyInnerShareable();
}
ALWAYS_INLINE void InvalidateEntireTlbDataOnly() {
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
DataSynchronizationBarrier();

View File

@@ -105,35 +105,48 @@ namespace ams::kern::arch::arm64 {
Result UnbindLocal(s32 irq);
Result ClearGlobal(s32 irq);
Result ClearLocal(s32 irq);
public:
static ALWAYS_INLINE u32 DisableInterrupts() {
private:
[[nodiscard]] static ALWAYS_INLINE u32 GetInterruptsEnabledState() {
u64 intr_state;
__asm__ __volatile__("mrs %[intr_state], daif\n"
"msr daifset, #2"
__asm__ __volatile__("mrs %[intr_state], daif\n"
"ubfx %[intr_state], %[intr_state], #7, #1"
: [intr_state]"=r"(intr_state)
:: "memory");
return intr_state;
}
public:
static ALWAYS_INLINE void EnableInterrupts() {
__asm__ __volatile__("msr daifclr, #2" ::: "memory");
}
static ALWAYS_INLINE u32 EnableInterrupts() {
u64 intr_state;
__asm__ __volatile__("mrs %[intr_state], daif\n"
"msr daifclr, #2"
: [intr_state]"=r"(intr_state)
:: "memory");
static ALWAYS_INLINE void DisableInterrupts() {
__asm__ __volatile__("msr daifset, #2" ::: "memory");
}
[[nodiscard]] static ALWAYS_INLINE u32 GetInterruptsEnabledStateAndDisableInterrupts() {
const auto intr_state = GetInterruptsEnabledState();
DisableInterrupts();
return intr_state;
}
[[nodiscard]] static ALWAYS_INLINE u32 GetInterruptsEnabledStateAndEnableInterrupts() {
const auto intr_state = GetInterruptsEnabledState();
EnableInterrupts();
return intr_state;
}
static ALWAYS_INLINE void RestoreInterrupts(u32 intr_state) {
u64 cur_state;
__asm__ __volatile__("mrs %[cur_state], daif" : [cur_state]"=r"(cur_state));
__asm__ __volatile__("msr daif, %[intr_state]" :: [intr_state]"r"((cur_state & ~0x80ul) | (intr_state & 0x80)));
u64 tmp;
__asm__ __volatile__("mrs %[tmp], daif\n"
"bfi %[tmp], %[intr_state], #7, #1\n"
"msr daif, %[tmp]"
: [tmp]"=&r"(tmp)
: [intr_state]"r"(intr_state)
: "memory");
}
static ALWAYS_INLINE bool AreInterruptsEnabled() {
u64 intr_state;
__asm__ __volatile__("mrs %[intr_state], daif" : [intr_state]"=r"(intr_state));
return (intr_state & 0x80) == 0;
return GetInterruptsEnabledState() == 0;
}
};

View File

@@ -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);
static void PteDataSynchronizationBarrier() {
static ALWAYS_INLINE void PteDataSynchronizationBarrier() {
cpu::DataSynchronizationBarrierInnerShareable();
}
static void ClearPageTable(KVirtualAddress table) {
static ALWAYS_INLINE void ClearPageTable(KVirtualAddress table) {
cpu::ClearPageToZero(GetVoidPointer(table));
}
void OnTableUpdated() const {
ALWAYS_INLINE void OnTableUpdated() const {
cpu::InvalidateTlbByAsid(m_asid);
}
void OnKernelTableUpdated() const {
ALWAYS_INLINE void OnKernelTableUpdated() const {
cpu::InvalidateEntireTlbDataOnly();
}
void OnKernelTableSinglePageUpdated(KProcessAddress virt_addr) const {
ALWAYS_INLINE void OnKernelTableSinglePageUpdated(KProcessAddress virt_addr) const {
cpu::InvalidateTlbByVaDataOnly(virt_addr);
}
void NoteUpdated() const {
ALWAYS_INLINE void NoteUpdated() const {
cpu::DataSynchronizationBarrier();
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());
cpu::DataSynchronizationBarrier();

View File

@@ -45,6 +45,7 @@ namespace ams::kern::arch::arm64 {
/* Select L1 cache. */
cpu::SetCsselrEl1(0);
cpu::InstructionMemoryBarrier();
/* Check that the L1 cache is not direct-mapped. */
return cpu::CacheSizeIdRegisterAccessor().GetAssociativity() != 0;

View File

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

View File

@@ -46,7 +46,7 @@ namespace ams::kern {
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);
}
};

View File

@@ -211,18 +211,6 @@ namespace ams::kern {
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> {
public:
explicit ALWAYS_INLINE KScopedSchedulerLock() : KScopedLock(KScheduler::s_scheduler_lock) { /* ... */ }

View File

@@ -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);
}
}
}

View File

@@ -405,8 +405,6 @@ namespace ams::kern {
constexpr ThreadState GetState() const { return static_cast<ThreadState>(m_thread_state & ThreadState_Mask); }
constexpr ThreadState GetRawState() const { return m_thread_state; }
NOINLINE KThreadContext *GetContextForSchedulerLoop();
constexpr uintptr_t GetConditionVariableKey() const { return m_condvar_key; }
constexpr uintptr_t GetAddressArbiterKey() const { return m_condvar_key; }
@@ -624,9 +622,7 @@ namespace ams::kern {
void OnTimer();
void DoWorkerTaskImpl();
public:
static constexpr bool IsConditionVariableThreadTreeValid() {
return ConditionVariableThreadTreeTraits::IsValid();
}
static consteval bool IsKThreadStructurallyValid();
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);
@@ -634,7 +630,18 @@ namespace ams::kern {
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
};
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 {
public:

View File

@@ -41,7 +41,7 @@ namespace ams::kern {
private:
u32 m_prev_intr_state;
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); }
};
@@ -51,7 +51,7 @@ namespace ams::kern {
private:
u32 m_prev_intr_state;
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); }
};

View File

@@ -61,139 +61,3 @@ _ZN3ams4kern4arch5arm643cpu23SynchronizeAllCoresImplEPii:
5:
stlr wzr, [x0]
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

View File

@@ -225,7 +225,7 @@ namespace ams::kern::arch::arm64 {
if (AMS_UNLIKELY(GetCurrentThread().IsSingleStep())) {
GetCurrentThread().ClearSingleStep();
cpu::MonitorDebugSystemControlRegisterAccessor().SetSoftwareStep(false).Store();
cpu::EnsureInstructionConsistency();
cpu::InstructionMemoryBarrier();
}
#endif

View File

@@ -201,7 +201,7 @@ namespace ams::kern::arch::arm64 {
if (user_mode) {
KThread *cur_thread = GetCurrentThreadPointer();
if (cur_thread->IsTerminationRequested()) {
KScopedInterruptEnable ei;
EnableInterrupts();
cur_thread->Exit();
}
}
@@ -212,13 +212,14 @@ namespace ams::kern::arch::arm64 {
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
KScopedInterruptDisable di;
if (KInterruptController::IsGlobal(irq)) {
KScopedInterruptDisable di;
KScopedSpinLock lk(this->GetGlobalInterruptLock());
return this->BindGlobal(handler, irq, core_id, priority, manual_clear, level);
} else {
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
KScopedInterruptDisable di;
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());
KScopedInterruptDisable di;
if (KInterruptController::IsGlobal(irq)) {
KScopedInterruptDisable di;
KScopedSpinLock lk(this->GetGlobalInterruptLock());
return this->UnbindGlobal(irq);
} else {
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
KScopedInterruptDisable di;
return this->UnbindLocal(irq);
}
}
@@ -244,13 +248,15 @@ namespace ams::kern::arch::arm64 {
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
KScopedInterruptDisable di;
if (KInterruptController::IsGlobal(irq)) {
KScopedInterruptDisable di;
KScopedSpinLock lk(this->GetGlobalInterruptLock());
return this->ClearGlobal(irq);
} else {
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
KScopedInterruptDisable di;
return this->ClearLocal(irq);
}
}

View File

@@ -169,10 +169,10 @@ namespace ams::kern::arch::arm64 {
m_manager = std::addressof(Kernel::GetSystemPageTableManager());
/* 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 KVirtualAddress page = m_manager->Allocate();
MESOSPHERE_ASSERT(page != Null<KVirtualAddress>);
cpu::ClearPageToZero(GetVoidPointer(page));
m_ttbr = GetInteger(KPageTableBase::GetLinearMappedPhysicalAddress(page)) | asid_tag;
/* 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());
/* 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);
/* Note that we updated. */

View File

@@ -28,7 +28,7 @@ _ZN3ams4kern3svc25CallReturnFromException64Ev:
stp x20, x21, [sp, #(EXCEPTION_CONTEXT_X20_X21)]
stp x22, x23, [sp, #(EXCEPTION_CONTEXT_X22_X23)]
stp x24, x25, [sp, #(EXCEPTION_CONTEXT_X24_X25)]
stp x26, x26, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
stp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
/* Call ams::kern::arch::arm64::ReturnFromException(result). */

View File

@@ -50,10 +50,6 @@ _ZN3ams4kern3svc26CallSendSyncRequestLight64Ev:
.global _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev
.type _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev, %function
_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. */
sub sp, sp, #(4 * 8)
@@ -78,13 +74,8 @@ _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev:
/* Free the stack space for the light ipc data. */
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
/* ams::kern::svc::CallReplyAndReceiveLight64() */
.section .text._ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev, "ax", %progbits
.global _ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev
@@ -121,10 +112,6 @@ _ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev:
.global _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev
.type _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev, %function
_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. */
sub sp, sp, #(4 * 8)
@@ -149,8 +136,4 @@ _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev:
/* Free the stack space for the light ipc data. */
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

View File

@@ -656,9 +656,8 @@ namespace ams::kern::board::nintendo::nx {
MESOSPHERE_ASSERT(IsValidPhysicalAddress(table_phys_addr));
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. */
cpu::ClearPageToZero(GetVoidPointer(table_virt_addr));
cpu::StoreDataCache(GetVoidPointer(table_virt_addr), PageDirectorySize);
g_reserved_table_phys_addr = table_phys_addr;

View File

@@ -341,7 +341,9 @@ namespace ams::kern::board::nintendo::nx {
/* Restore pmu registers. */
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::SetPmIntEnClrEl1(static_cast<u64>(static_cast<u32>(~u32())));
cpu::SetPmCntEnClrEl0(static_cast<u64>(static_cast<u32>(~u32())));

View File

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

View File

@@ -79,13 +79,6 @@ namespace ams::kern {
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) {
if (KThread *prev_highest_thread = m_state.highest_priority_thread; AMS_LIKELY(prev_highest_thread != highest_thread)) {
if (AMS_LIKELY(prev_highest_thread != nullptr)) {
@@ -254,9 +247,24 @@ namespace ams::kern {
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. */
cpu::MonitorDebugSystemControlRegisterAccessor().SetSoftwareStep(next_thread->IsSingleStep()).Store();
#endif
/* Switch the current process, if we're switching processes. */
if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) {
KProcess::Switch(cur_process, next_process);
} else {
/* The single-step bit set up above requires an instruction synchronization barrier, to ensure */
/* the state change takes before we actually perform a return which might break-to-step. */
/* KProcess::Switch performs an isb incidentally, and so when we're changing process we */
/* can piggy-back off of that isb to avoid unnecessarily emptying the pipeline twice. */
/* However, this means that when we're switching to thread in a different process, */
/* we must ensure that we still isb. In practice, gcc will deduplicate into a single isb. */
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
cpu::InstructionMemoryBarrier();
#endif
}
/* Set the new thread. */

View File

@@ -1315,10 +1315,6 @@ namespace ams::kern {
}
}
KThreadContext *KThread::GetContextForSchedulerLoop() {
return std::addressof(this->GetContext());
}
KThread *KThread::GetThreadFromId(u64 thread_id) {
/* Lock the list. */
KThread::ListAccessor accessor;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -89,9 +89,8 @@ namespace ams::fs {
Result DeleteSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) {
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id);
/* TODO: Libnx binding for DeleteSaveDataFileSystemBySaveDataAttribute */
AMS_UNUSED(space_id, attribute);
AMS_ABORT();
static_assert(sizeof(attribute) == sizeof(::FsSaveDataAttribute));
return fsDeleteSaveDataFileSystemBySaveDataAttribute(static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute)));
}
Result GetSaveDataFlags(u32 *out, SaveDataId id) {

View File

@@ -20,6 +20,8 @@ extern "C" {
constinit u32 __nx_fs_num_sessions = 1;
constinit u32 __nx_applet_type = AppletType_None;
constinit bool __nx_fsdev_support_cwd = false;
extern int __system_argc;
extern char** __system_argv;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,9 +18,9 @@
#include "sprofile_srv_i_profile_importer.hpp"
#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, 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, 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, 100, Result, OpenProfileImporter, (sf::Out<sf::SharedPointer<::ams::sprofile::IProfileImporter>> out), (out)) \
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, 2000, Result, Reset, (), ())
AMS_SF_DEFINE_INTERFACE(ams::sprofile, ISprofileServiceForBgAgent, AMS_SPROFILE_I_SPROFILE_SERVICE_FOR_BG_AGENT_INTERFACE_INFO)

View File

@@ -26,6 +26,10 @@ namespace ams::sprofile::srv {
ProfileManager *m_manager;
public:
ProfileImporterImpl(ProfileManager *manager) : m_manager(manager) { /* ... */ }
~ProfileImporterImpl() {
m_manager->CloseProfileImporter();
}
public:
Result ImportProfile(const sprofile::srv::ProfileDataForImportData &import);
Result Commit();

View File

@@ -151,6 +151,16 @@ namespace ams::sprofile::srv {
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) {
/* Acquire locks. */
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. */
{
crypto::Md5Generator md5;
md5.Initialize();
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))));
@@ -258,9 +270,11 @@ namespace ams::sprofile::srv {
/* Check that the metadata we're importing has a valid hash. */
{
crypto::Md5Generator md5;
md5.Initialize();
md5.Update(std::addressof(import.header), sizeof(import.header));
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];
md5.GetHash(hash, sizeof(hash));

View File

@@ -50,6 +50,8 @@ namespace ams::sprofile::srv {
Result ResetSaveData();
Result OpenProfileImporter();
void CloseProfileImporter();
Result ImportProfile(const sprofile::srv::ProfileDataForImportData &data);
Result Commit();
Result ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data);

View File

@@ -34,7 +34,7 @@ namespace ams::sprofile::srv {
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. */
R_UNLESS(out.GetSize() >= arg.metadata.num_entries, sprofile::ResultInvalidArgument());
@@ -42,25 +42,31 @@ namespace ams::sprofile::srv {
sprofile::srv::ProfileMetadata primary_metadata;
R_TRY_CATCH(m_profile_manager->LoadPrimaryMetadata(std::addressof(primary_metadata))) {
R_CATCH(fs::ResultPathNotFound) {
/* If we have no metadata, we can't get any entries. */
*out_count = 0;
return ResultSuccess();
/* It's okay if we have no primary metadata -- this means that all profiles are importable. */
primary_metadata.num_entries = 0;
}
} 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;
for (u32 i = 0; i < arg.metadata.num_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) {
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) {
out[count++] = arg.entries[i];
have_entry = true;
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. */
@@ -68,6 +74,7 @@ namespace ams::sprofile::srv {
return ResultSuccess();
}
Result ServiceForBgAgent::IsUpdateNeeded(sf::Out<bool> out, Identifier revision_key) {
/* Load primary metadata. */
bool loaded_metadata = true;

View File

@@ -29,7 +29,7 @@ namespace ams::sprofile::srv {
constexpr ServiceForBgAgent(MemoryResource *mr, ProfileManager *pm) : m_memory_resource(mr), m_profile_manager(pm) { /* ... */ }
public:
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 Reset();
};

View File

@@ -46,6 +46,10 @@ namespace ams::sprofile::srv {
static_assert(util::is_pod<ProfileDataEntry>::value);
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 {
u32 num_entries;
u8 unk_04[0x0C];
@@ -55,6 +59,11 @@ namespace ams::sprofile::srv {
static_assert(util::is_pod<ProfileData>::value);
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 {
Identifier name;
ProfileData data;
@@ -62,6 +71,9 @@ namespace ams::sprofile::srv {
static_assert(util::is_pod<ServiceProfile>::value);
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 {
Identifier identifier_0;
@@ -77,6 +89,11 @@ namespace ams::sprofile::srv {
static_assert(util::is_pod<ProfileDataForImportData>::value);
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 {
Identifier identifier_0;
Identifier identifier_1;
@@ -85,22 +102,34 @@ namespace ams::sprofile::srv {
static_assert(util::is_pod<ProfileMetadataEntry>::value);
static_assert(sizeof(ProfileMetadataEntry) == 0x40);
struct ProfileMetadataEntryData : public sf::PrefersMapAliasTransferMode {
u8 unk[0x100];
static_assert(AMS_OFFSETOF(ProfileMetadataEntry, identifier_0) == 0x00);
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(sizeof(ProfileMetadataEntryData) == 0x100);
static_assert(util::is_pod<ProfileUrl>::value);
static_assert(sizeof(ProfileUrl) == 0x100);
struct ProfileMetadata {
u32 num_entries;
u32 unk_04;
Identifier revision_key;
u8 unk_0F[0x1];
u8 unk_10[0x30];
ProfileMetadataEntry entries[50];
};
static_assert(util::is_pod<ProfileMetadata>::value);
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 {
u32 version;
@@ -108,10 +137,16 @@ namespace ams::sprofile::srv {
} header;
u8 hash[crypto::Md5Generator::HashSize];
ProfileMetadata metadata;
ProfileMetadataEntryData entries[50];
u8 unk[0x8000 - 0x3EF0];
ProfileUrl profile_urls[50];
u8 unk_3EF0[0x8000 - 0x3EF0];
};
static_assert(util::is_pod<ProfileMetadataForImportMetadata>::value);
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);
}

View File

@@ -17,7 +17,7 @@
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 1
#define ATMOSPHERE_RELEASE_VERSION_MINOR 2
#define ATMOSPHERE_RELEASE_VERSION_MICRO 1
#define ATMOSPHERE_RELEASE_VERSION_MICRO 3
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO

View File

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

View File

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

View File

@@ -149,7 +149,7 @@ namespace ams::crypto::impl {
/* Copy in any leftover data. */
if (const auto left = size % BlockSize; left > 0) {
std::memcpy(m_y, cur_block, size);
std::memcpy(m_y, cur_block, left);
}
}

View File

@@ -49,10 +49,9 @@ namespace ams::kern::init {
constexpr PageTableEntry KernelRwDataUncachedAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemoryNotCacheable, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped);
void StoreDataCache(const void *addr, size_t size) {
uintptr_t start = util::AlignDown(reinterpret_cast<uintptr_t>(addr), cpu::DataCacheLineSize);
uintptr_t end = reinterpret_cast<uintptr_t>(addr) + size;
for (uintptr_t cur = start; cur < end; cur += cpu::DataCacheLineSize) {
__asm__ __volatile__("dc cvac, %[cur]" :: [cur]"r"(cur) : "memory");
const uintptr_t start = util::AlignDown(reinterpret_cast<uintptr_t>(addr), cpu::DataCacheLineSize);
for (size_t stored = 0; stored < size; stored += cpu::DataCacheLineSize) {
__asm__ __volatile__("dc cvac, %[cur]" :: [cur]"r"(start + stored) : "memory");
}
cpu::DataSynchronizationBarrier();
}
@@ -594,11 +593,13 @@ namespace ams::kern::init {
switch (num_watchpoints) {
FOR_I_IN_15_TO_1(MESOSPHERE_INITIALIZE_WATCHPOINT_CASE, 0)
case 0:
cpu::SetDbgWcr0El1(0);
cpu::SetDbgWvr0El1(0);
[[fallthrough]];
default:
break;
}
cpu::SetDbgWcr0El1(0);
cpu::SetDbgWvr0El1(0);
switch (num_breakpoints) {
FOR_I_IN_15_TO_1(MESOSPHERE_INITIALIZE_BREAKPOINT_CASE, 0)

View File

@@ -376,7 +376,7 @@ _ZN3ams4kern4arch5arm6430EL1SynchronousExceptionHandlerEv:
stp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
mrs x20, sp_el0
ldr x20, [sp]
mrs x21, elr_el1
mrs x22, spsr_el1
mrs x23, tpidr_el0
@@ -529,7 +529,7 @@ _ZN3ams4kern4arch5arm6421EL1SystemErrorHandlerEv:
stp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
mrs x20, sp_el0
ldr x20, [sp]
mrs x21, elr_el1
mrs x22, spsr_el1
mrs x23, tpidr_el0

View File

@@ -183,9 +183,7 @@ _ZN3ams4kern10KScheduler12ScheduleImplEv:
/* Get the highest priority thread's context, and save it. */
/* ams::kern::KThread::GetContextForSchedulerLoop() */
mov x0, x21
bl _ZN3ams4kern7KThread26GetContextForSchedulerLoopEv
mov x22, x0
add x22, x21, #(THREAD_THREAD_CONTEXT)
/* Prepare to try to acquire the context lock. */
add x1, x22, #(THREAD_CONTEXT_LOCKED)
@@ -227,26 +225,7 @@ _ZN3ams4kern10KScheduler12ScheduleImplEv:
mov x0, x22
RESTORE_THREAD_CONTEXT(x0, x1, x2, 9f)
9: /* Configure single-step, if we should. */
#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. */
9: /* We're done restoring the thread context, and can return safely. */
ret
10: /* Our switch failed. */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,7 @@
"filesystem_access": {
"permissions": "0xFFFFFFFFFFFFFFFF"
},
"service_access": ["pm:dmnt", "ldr:dmnt", "ro:dmnt", "ns:dev", "lr", "fsp-srv", "fatal:u", "pgl", "htcs"],
"service_access": ["pm:dmnt", "ldr:dmnt", "ro:dmnt", "ns:dev", "lr", "fsp-srv", "fatal:u", "pgl", "htcs", "bsd:s", "set:sys"],
"service_host": [],
"kernel_capabilities": [{
"type": "kernel_flags",

View File

@@ -16,7 +16,8 @@
#include <stratosphere.hpp>
#include "dmnt2_debug_log.hpp"
#define AMS_DMNT2_ENABLE_HTCS_DEBUG_LOG
// TODO: This should be converted to use log manager. */
//#define AMS_DMNT2_ENABLE_HTCS_DEBUG_LOG
#if defined(AMS_DMNT2_ENABLE_HTCS_DEBUG_LOG)
@@ -164,15 +165,78 @@ namespace ams::dmnt {
namespace ams::dmnt {
//#define AMS_DMNT2_ENABLE_SD_CARD_DEBUG_LOG
#if defined(AMS_DMNT2_ENABLE_SD_CARD_DEBUG_LOG)
namespace {
alignas(0x40) constinit u8 g_buffer[os::MemoryPageSize * 4];
constinit lmem::HeapHandle g_debug_log_heap;
constinit fs::FileHandle g_debug_log_file;
constinit os::SdkMutex g_fs_mutex;
constinit s64 g_fs_offset = 0;
void *Allocate(size_t size) {
return lmem::AllocateFromExpHeap(g_debug_log_heap, size);
}
void Deallocate(void *p, size_t size) {
AMS_UNUSED(size);
return lmem::FreeToExpHeap(g_debug_log_heap, p);
}
}
void InitializeDebugLog() {
/* Do nothing. */
g_debug_log_heap = lmem::CreateExpHeap(g_buffer, sizeof(g_buffer), lmem::CreateOption_ThreadSafe);
fs::SetAllocator(Allocate, Deallocate);
fs::InitializeForSystem();
fs::SetEnabledAutoAbort(false);
R_ABORT_UNLESS(fs::MountSdCard("sdmc"));
fs::DeleteFile("sdmc:/dmnt2.log");
R_ABORT_UNLESS(fs::CreateFile("sdmc:/dmnt2.log", 0));
R_ABORT_UNLESS(fs::OpenFile(std::addressof(g_debug_log_file), "sdmc:/dmnt2.log", fs::OpenMode_Write | fs::OpenMode_AllowAppend));
}
void DebugLog(const char *prefix, const char *fmt, ...) {
/* Do nothing. */
char buffer[0x200];
{
const auto prefix_len = std::strlen(prefix);
std::memcpy(buffer, prefix, prefix_len);
std::va_list vl;
va_start(vl, fmt);
util::VSNPrintf(buffer + prefix_len, sizeof(buffer) - prefix_len, fmt, vl);
va_end(vl);
}
const auto len = std::strlen(buffer);
std::scoped_lock lk(g_fs_mutex);
R_ABORT_UNLESS(fs::WriteFile(g_debug_log_file, g_fs_offset, buffer, len, fs::WriteOption::Flush));
g_fs_offset += len;
}
#else
void InitializeDebugLog() {
/* ... */
}
void DebugLog(const char *prefix, const char *fmt, ...) {
AMS_UNUSED(prefix, fmt);
}
#endif
}

View File

@@ -27,10 +27,15 @@ namespace ams::dmnt {
}
Result DebugProcess::Attach(os::ProcessId process_id) {
Result DebugProcess::Attach(os::ProcessId process_id, bool start_process) {
/* Attach to the process. */
R_TRY(svc::DebugActiveProcess(std::addressof(m_debug_handle), process_id.value));
/* If necessary, start the process. */
if (start_process) {
R_ABORT_UNLESS(pm::dmnt::StartProcess(process_id));
}
/* Collect initial information. */
R_TRY(this->Start());
@@ -42,6 +47,10 @@ namespace ams::dmnt {
svc::GetProcessId(std::addressof(pid_value), m_debug_handle);
m_process_id = { pid_value };
/* Get process info. */
this->CollectProcessInfo();
return ResultSuccess();
}
@@ -187,13 +196,25 @@ namespace ams::dmnt {
char path[ModuleDefinition::PathLengthMax];
} module_path;
if (R_SUCCEEDED(this->ReadMemory(std::addressof(module_path), memory_info.base_address + memory_info.size, sizeof(module_path)))) {
if (module_path.zero == 0 && module_path.path_length == util::Strnlen(module_path.path, sizeof(module_path.path))) {
std::memcpy(module_name, module_path.path, ModuleDefinition::PathLengthMax);
if (module_path.zero == 0 && module_path.path_length > 0) {
std::memcpy(module_name, module_path.path, std::min<size_t>(ModuleDefinition::PathLengthMax, module_path.path_length));
}
} else {
module_path.path_length = 0;
}
/* Truncate module name. */
module_name[ModuleDefinition::PathLengthMax - 1] = 0;
/* Set default module name start. */
module.SetNameStart(0);
/* Ignore leading directories. */
for (size_t i = 0; i < std::min<size_t>(ModuleDefinition::PathLengthMax, module_path.path_length) && module_name[i] != 0; ++i) {
if (module_name[i] == '/' || module_name[i] == '\\') {
module.SetNameStart(i + 1);
}
}
}
}
@@ -212,6 +233,53 @@ namespace ams::dmnt {
return ResultSuccess();
}
void DebugProcess::CollectProcessInfo() {
/* Define helper for getting process info. */
auto CollectProcessInfoImpl = [&](os::NativeHandle handle) -> Result {
/* Collect all values. */
R_TRY(svc::GetInfo(std::addressof(m_process_alias_address), svc::InfoType_AliasRegionAddress, handle, 0));
R_TRY(svc::GetInfo(std::addressof(m_process_alias_size), svc::InfoType_AliasRegionSize, handle, 0));
R_TRY(svc::GetInfo(std::addressof(m_process_heap_address), svc::InfoType_HeapRegionAddress, handle, 0));
R_TRY(svc::GetInfo(std::addressof(m_process_heap_size), svc::InfoType_HeapRegionSize, handle, 0));
R_TRY(svc::GetInfo(std::addressof(m_process_aslr_address), svc::InfoType_AslrRegionAddress, handle, 0));
R_TRY(svc::GetInfo(std::addressof(m_process_aslr_size), svc::InfoType_AslrRegionSize, handle, 0));
R_TRY(svc::GetInfo(std::addressof(m_process_stack_address), svc::InfoType_StackRegionAddress, handle, 0));
R_TRY(svc::GetInfo(std::addressof(m_process_stack_size), svc::InfoType_StackRegionSize, handle, 0));
if (m_program_location.program_id == ncm::InvalidProgramId) {
R_TRY(svc::GetInfo(std::addressof(m_program_location.program_id.value), svc::InfoType_ProgramId, handle, 0));
}
u64 value;
R_TRY(svc::GetInfo(std::addressof(value), svc::InfoType_IsApplication, handle, 0));
m_is_application = value != 0;
return ResultSuccess();
};
/* Get process info/status. */
os::NativeHandle process_handle;
if (R_FAILED(pm::dmnt::AtmosphereGetProcessInfo(std::addressof(process_handle), std::addressof(m_program_location), std::addressof(m_process_override_status), m_process_id))) {
process_handle = os::InvalidNativeHandle;
m_program_location = { ncm::InvalidProgramId, };
m_process_override_status = {};
}
ON_SCOPE_EXIT { os::CloseNativeHandle(process_handle); };
/* Try collecting from our debug handle, then the process handle. */
if (R_FAILED(CollectProcessInfoImpl(m_debug_handle)) && R_FAILED(CollectProcessInfoImpl(process_handle))) {
m_process_alias_address = 0;
m_process_alias_size = 0;
m_process_heap_address = 0;
m_process_heap_size = 0;
m_process_aslr_address = 0;
m_process_aslr_size = 0;
m_process_stack_address = 0;
m_process_stack_size = 0;
m_is_application = false;
}
}
Result DebugProcess::GetThreadContext(svc::ThreadContext *out, u64 thread_id, u32 flags) {
return svc::GetDebugThreadContext(out, m_debug_handle, thread_id, flags);
}
@@ -228,6 +296,11 @@ namespace ams::dmnt {
return svc::WriteDebugProcessMemory(m_debug_handle, reinterpret_cast<uintptr_t>(src), address, size);
}
Result DebugProcess::QueryMemory(svc::MemoryInfo *out, uintptr_t address) {
svc::PageInfo dummy;
return svc::QueryDebugProcessMemory(out, std::addressof(dummy), m_debug_handle, address);
}
Result DebugProcess::Continue() {
AMS_DMNT2_GDB_LOG_DEBUG("DebugProcess::Continue() all\n");
@@ -522,5 +595,25 @@ namespace ams::dmnt {
return ResultSuccess();
}
void DebugProcess::GetThreadName(char *dst, u64 thread_id) const {
for (size_t i = 0; i < ThreadCountMax; ++i) {
if (m_thread_valid[i] && m_thread_ids[i] == thread_id) {
if (R_FAILED(osdbg::GetThreadName(dst, std::addressof(m_thread_infos[i])))) {
if (m_thread_infos[i]._thread_type != 0) {
if (m_thread_infos[i]._thread_type_type == osdbg::ThreadTypeType_Libnx) {
util::TSNPrintf(dst, os::ThreadNameLengthMax, "libnx Thread_0x%010lx", reinterpret_cast<uintptr_t>(m_thread_infos[i]._thread_type));
} else {
util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_0x%010lx", reinterpret_cast<uintptr_t>(m_thread_infos[i]._thread_type));
}
} else {
break;
}
}
return;
}
}
util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_ID=%lu", thread_id);
}
}

View File

@@ -50,6 +50,7 @@ namespace ams::dmnt {
u64 m_last_thread_id{};
u64 m_thread_id_override{};
u64 m_continue_thread_id{};
u64 m_preferred_debug_break_thread_id{};
GdbSignal m_last_signal{};
bool m_stepping{false};
bool m_use_hardware_single_step{false};
@@ -64,6 +65,17 @@ namespace ams::dmnt {
ModuleDefinition m_module_definitions[ModuleCountMax]{};
size_t m_module_count{};
size_t m_main_module{};
u64 m_process_alias_address{};
u64 m_process_alias_size{};
u64 m_process_heap_address{};
u64 m_process_heap_size{};
u64 m_process_aslr_address{};
u64 m_process_aslr_size{};
u64 m_process_stack_address{};
u64 m_process_stack_size{};
ncm::ProgramLocation m_program_location{};
cfg::OverrideStatus m_process_override_status{};
bool m_is_application{false};
public:
DebugProcess() : m_software_breakpoints(this), m_hardware_breakpoints(this), m_hardware_watchpoints(this), m_step_breakpoints(m_software_breakpoints) {
if (svc::IsKernelMesosphere()) {
@@ -80,7 +92,8 @@ namespace ams::dmnt {
size_t GetModuleCount() const { return m_module_count; }
size_t GetMainModuleIndex() const { return m_main_module; }
const char *GetModuleName(size_t ix) const { return m_module_definitions[ix].GetName(); }
uintptr_t GetBaseAddress(size_t ix) const { return m_module_definitions[ix].GetAddress(); }
uintptr_t GetModuleBaseAddress(size_t ix) const { return m_module_definitions[ix].GetAddress(); }
uintptr_t GetModuleSize(size_t ix) const { return m_module_definitions[ix].GetSize(); }
ProcessStatus GetStatus() const { return m_status; }
os::ProcessId GetProcessId() const { return m_process_id; }
@@ -95,20 +108,37 @@ namespace ams::dmnt {
u64 GetLastThreadId();
u64 GetThreadIdOverride() { this->GetLastThreadId(); return m_thread_id_override; }
u64 GetPreferredDebuggerBreakThreadId() { return m_preferred_debug_break_thread_id; }
void SetLastThreadId(u64 tid) {
m_last_thread_id = tid;
m_thread_id_override = tid;
}
void SetThreadIdOverride(u64 tid) {
m_thread_id_override = tid;
m_thread_id_override = tid;
m_preferred_debug_break_thread_id = tid;
}
void SetDebugBreaked() {
m_status = ProcessStatus_DebugBreak;
}
u64 GetAliasRegionAddress() const { return m_process_alias_address; }
u64 GetAliasRegionSize() const { return m_process_alias_size; }
u64 GetHeapRegionAddress() const { return m_process_heap_address; }
u64 GetHeapRegionSize() const { return m_process_heap_size; }
u64 GetAslrRegionAddress() const { return m_process_aslr_address; }
u64 GetAslrRegionSize() const { return m_process_aslr_size; }
u64 GetStackRegionAddress() const { return m_process_stack_address; }
u64 GetStackRegionSize() const { return m_process_stack_size; }
const ncm::ProgramLocation &GetProgramLocation() const { return m_program_location; }
const cfg::OverrideStatus &GetOverrideStatus() const { return m_process_override_status; }
bool IsApplication() const { return m_is_application; }
public:
Result Attach(os::ProcessId process_id);
Result Attach(os::ProcessId process_id, bool start_process = false);
void Detach();
Result GetThreadContext(svc::ThreadContext *out, u64 thread_id, u32 flags);
@@ -117,6 +147,8 @@ namespace ams::dmnt {
Result ReadMemory(void *dst, uintptr_t address, size_t size);
Result WriteMemory(const void *src, uintptr_t address, size_t size);
Result QueryMemory(svc::MemoryInfo *out, uintptr_t address);
Result Continue();
Result Continue(u64 thread_id);
Result Step();
@@ -146,9 +178,13 @@ namespace ams::dmnt {
void GetBranchTarget(svc::ThreadContext &ctx, u64 thread_id, u64 &current_pc, u64 &target);
Result CollectModules();
void GetThreadName(char *dst, u64 thread_id) const;
private:
Result Start();
void CollectProcessInfo();
s32 ThreadCreate(u64 thread_id);
void ThreadExit(u64 thread_id);
};

View File

@@ -41,7 +41,7 @@ namespace ams::dmnt {
}
void GdbPacketIo::SendPacket(bool *out_break, const char *src, HtcsSession *session) {
void GdbPacketIo::SendPacket(bool *out_break, const char *src, TransportSession *session) {
/* Default to not breaked. */
*out_break = false;
@@ -99,7 +99,7 @@ namespace ams::dmnt {
}
}
char *GdbPacketIo::ReceivePacket(bool *out_break, char *dst, size_t size, HtcsSession *session) {
char *GdbPacketIo::ReceivePacket(bool *out_break, char *dst, size_t size, TransportSession *session) {
/* Default to not breaked. */
*out_break = false;

View File

@@ -15,11 +15,11 @@
*/
#pragma once
#include <stratosphere.hpp>
#include "dmnt2_htcs_session.hpp"
#include "dmnt2_transport_session.hpp"
namespace ams::dmnt {
static constexpr size_t GdbPacketBufferSize = 16_KB;
static constexpr size_t GdbPacketBufferSize = 32_KB;
class GdbPacketIo {
private:
@@ -30,8 +30,8 @@ namespace ams::dmnt {
void SetNoAck() { m_no_ack = true; }
void SendPacket(bool *out_break, const char *src, HtcsSession *session);
char *ReceivePacket(bool *out_break, char *dst, size_t size, HtcsSession *session);
void SendPacket(bool *out_break, const char *src, TransportSession *session);
char *ReceivePacket(bool *out_break, char *dst, size_t size, TransportSession *session);
};
}

View File

@@ -15,6 +15,7 @@
*/
#include <stratosphere.hpp>
#include "dmnt2_debug_log.hpp"
#include "dmnt2_transport_layer.hpp"
#include "dmnt2_gdb_server.hpp"
#include "dmnt2_gdb_server_impl.hpp"
@@ -36,34 +37,28 @@ namespace ams::dmnt {
while (true) {
/* Get a socket. */
int fd;
while ((fd = htcs::Socket()) == -1) {
while ((fd = transport::Socket()) == -1) {
os::SleepThread(TimeSpan::FromSeconds(1));
}
/* Ensure we cleanup the socket when we're done with it. */
ON_SCOPE_EXIT {
htcs::Close(fd);
transport::Close(fd);
os::SleepThread(TimeSpan::FromSeconds(1));
};
/* Create a sock addr for our server. */
htcs::SockAddrHtcs addr;
addr.family = htcs::HTCS_AF_HTCS;
addr.peer_name = htcs::GetPeerNameAny();
std::strcpy(addr.port_name.name, "iywys@$gdb");
/* Bind. */
if (htcs::Bind(fd, std::addressof(addr)) == -1) {
if (transport::Bind(fd, transport::PortName_GdbServer) == -1) {
continue;
}
/* Listen on our port. */
while (htcs::Listen(fd, 0) == 0) {
while (transport::Listen(fd, 0) == 0) {
/* Continue accepting clients, so long as we can. */
int client_fd;
while (true) {
/* Try to accept a client. */
if (client_fd = htcs::Accept(fd, std::addressof(addr)); client_fd < 0) {
if (client_fd = transport::Accept(fd); client_fd < 0) {
break;
}
@@ -77,7 +72,7 @@ namespace ams::dmnt {
}
/* Close the client socket. */
htcs::Close(client_fd);
transport::Close(client_fd);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -31,10 +31,11 @@ namespace ams::dmnt {
};
private:
int m_socket;
HtcsSession m_session;
TransportSession m_session;
GdbPacketIo m_packet_io;
char *m_receive_packet{nullptr};
char *m_reply_packet{nullptr};
char *m_reply_cur{nullptr};
char *m_reply_end{nullptr};
char m_buffer[GdbPacketBufferSize / 2];
bool m_killed{false};
os::ThreadType m_events_thread;
@@ -42,6 +43,7 @@ namespace ams::dmnt {
DebugProcess m_debug_process;
os::ProcessId m_process_id{os::InvalidProcessId};
os::Event m_event;
os::ProcessId m_wait_process_id{os::InvalidProcessId};
public:
GdbServerImpl(int socket, void *thread_stack, size_t stack_size);
~GdbServerImpl();
@@ -60,7 +62,7 @@ namespace ams::dmnt {
static void DebugEventsThreadEntry(void *arg) { static_cast<GdbServerImpl *>(arg)->DebugEventsThread(); }
void DebugEventsThread();
void ProcessDebugEvents();
void SetStopReplyPacket(GdbSignal signal);
void AppendStopReplyPacket(GdbSignal signal);
private:
void D();
@@ -98,6 +100,7 @@ namespace ams::dmnt {
void qAttached();
void qC();
void qRcmd();
void qSupported();
void qXfer();
void qXferFeaturesRead();

View File

@@ -16,15 +16,22 @@
#include <stratosphere.hpp>
#include "dmnt2_debug_log.hpp"
#include "dmnt2_gdb_server.hpp"
#include "dmnt2_transport_layer.hpp"
namespace ams {
namespace dmnt {
namespace {
namespace {
alignas(0x40) constinit u8 g_htcs_buffer[4_KB];
bool IsHtcEnabled() {
u8 enable_htc = 0;
settings::fwdbg::GetSettingsItemValue(std::addressof(enable_htc), sizeof(enable_htc), "atmosphere", "enable_htc");
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;
}
}
@@ -35,6 +42,9 @@ namespace ams {
/* Initialize our connection to sm. */
R_ABORT_UNLESS(sm::Initialize());
/* Initialize other services we need. */
R_ABORT_UNLESS(pmdmntInitialize());
/* Verify that we can sanely execute. */
ams::CheckApiVersion();
}
@@ -50,11 +60,23 @@ namespace ams {
os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(dmnt, Main));
AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(dmnt, Main));
/* Initialize htcs. */
constexpr auto HtcsSocketCountMax = 8;
const size_t buffer_size = htcs::GetWorkingMemorySize(HtcsSocketCountMax);
AMS_ABORT_UNLESS(sizeof(dmnt::g_htcs_buffer) >= buffer_size);
htcs::InitializeForSystem(dmnt::g_htcs_buffer, buffer_size, HtcsSocketCountMax);
bool use_htcs = false, use_tcp = false;
{
R_ABORT_UNLESS(::setsysInitialize());
ON_SCOPE_EXIT { ::setsysExit(); };
use_htcs = IsHtcEnabled();
use_tcp = IsStandaloneGdbstubEnabled();
}
/* Initialize transport layer. */
if (use_htcs) {
dmnt::transport::InitializeByHtcs();
} else if (use_tcp) {
dmnt::transport::InitializeByTcp();
} else {
return;
}
/* Initialize debug log thread. */
dmnt::InitializeDebugLog();

View File

@@ -26,6 +26,7 @@ namespace ams::dmnt {
char m_name[PathLengthMax];
u64 m_address;
u64 m_size;
size_t m_name_start;
public:
constexpr ModuleDefinition() : m_name(), m_address(), m_size() { /* ... */ }
constexpr ~ModuleDefinition() { /* ... */ }
@@ -34,9 +35,10 @@ namespace ams::dmnt {
constexpr ModuleDefinition &operator=(const ModuleDefinition &rhs) = default;
constexpr void Reset() {
m_name[0] = 0;
m_address = 0;
m_size = 0;
m_name[0] = 0;
m_address = 0;
m_size = 0;
m_name_start = 0;
}
constexpr bool operator==(const ModuleDefinition &rhs) const {
@@ -48,7 +50,7 @@ namespace ams::dmnt {
}
constexpr char *GetNameBuffer() { return m_name; }
constexpr const char *GetName() const { return m_name; }
constexpr const char *GetName() const { return m_name + m_name_start; }
constexpr u64 GetAddress() const { return m_address; }
constexpr u64 GetSize() const { return m_size; }
@@ -57,6 +59,10 @@ namespace ams::dmnt {
m_address = address;
m_size = size;
}
constexpr void SetNameStart(size_t offset) {
m_name_start = offset;
}
};
}

View File

@@ -0,0 +1,196 @@
/*
* 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/>.
*/
#include <stratosphere.hpp>
#include "dmnt2_transport_layer.hpp"
namespace ams::dmnt::transport {
namespace {
enum SocketMode {
SocketMode_Invalid,
SocketMode_Htcs,
SocketMode_Tcp,
};
constexpr inline const u16 ListenPort_GdbServer = 22225;
constexpr inline const u16 ListenPort_GdbDebugLog = 22227;
constinit os::SdkMutex g_socket_init_mutex;
constinit SocketMode g_socket_mode = SocketMode_Invalid;
constexpr inline size_t RequiredAlignment = std::max(os::ThreadStackAlignment, os::MemoryPageSize);
using SocketConfigType = socket::SystemConfigLightDefault;
/* TODO: If we ever use resolvers, increase this. */
constexpr inline size_t SocketAllocatorSize = 4_KB;
constexpr inline size_t SocketMemoryPoolSize = util::AlignUp(SocketConfigType::PerTcpSocketWorstCaseMemoryPoolSize + SocketConfigType::PerUdpSocketWorstCaseMemoryPoolSize, os::MemoryPageSize);
constexpr inline size_t SocketRequiredSize = util::AlignUp(SocketMemoryPoolSize + SocketAllocatorSize, os::MemoryPageSize);
/* Declare the memory pool. */
alignas(RequiredAlignment) constinit u8 g_socket_memory[SocketRequiredSize];
constexpr inline const SocketConfigType SocketConfig(g_socket_memory, SocketRequiredSize, SocketAllocatorSize, 2);
}
void InitializeByHtcs() {
std::scoped_lock lk(g_socket_init_mutex);
AMS_ABORT_UNLESS(g_socket_mode == SocketMode_Invalid);
constexpr auto HtcsSocketCountMax = 8;
const size_t buffer_size = htcs::GetWorkingMemorySize(HtcsSocketCountMax);
AMS_ABORT_UNLESS(sizeof(g_socket_memory) >= buffer_size);
htcs::InitializeForSystem(g_socket_memory, sizeof(g_socket_memory), HtcsSocketCountMax);
g_socket_mode = SocketMode_Htcs;
}
void InitializeByTcp() {
std::scoped_lock lk(g_socket_init_mutex);
AMS_ABORT_UNLESS(g_socket_mode == SocketMode_Invalid);
R_ABORT_UNLESS(socket::Initialize(SocketConfig));
g_socket_mode = SocketMode_Tcp;
}
s32 Socket() {
switch (g_socket_mode) {
case SocketMode_Htcs: return htcs::Socket();
case SocketMode_Tcp: return socket::Socket(socket::Family::Af_Inet, socket::Type::Sock_Stream, socket::Protocol::IpProto_Tcp);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
s32 Close(s32 desc) {
switch (g_socket_mode) {
case SocketMode_Htcs: return htcs::Close(desc);
case SocketMode_Tcp: return socket::Close(desc);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
s32 Bind(s32 desc, PortName port_name) {
switch (g_socket_mode) {
case SocketMode_Htcs:
{
htcs::SockAddrHtcs addr;
addr.family = htcs::HTCS_AF_HTCS;
addr.peer_name = htcs::GetPeerNameAny();
switch (port_name) {
case PortName_GdbServer: std::strcpy(addr.port_name.name, "iywys@$gdb"); break;
case PortName_GdbDebugLog: std::strcpy(addr.port_name.name, "iywys@$dmnt2_log"); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
return htcs::Bind(desc, std::addressof(addr));
}
break;
case SocketMode_Tcp:
{
socket::SockAddrIn addr = {};
addr.sin_family = socket::Family::Af_Inet;
addr.sin_addr.s_addr = socket::InAddr_Any;
switch (port_name){
case PortName_GdbServer: addr.sin_port = socket::InetHtons(static_cast<u16>(ListenPort_GdbServer)); break;
case PortName_GdbDebugLog: addr.sin_port = socket::InetHtons(static_cast<u16>(ListenPort_GdbDebugLog)); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
return socket::Bind(desc, reinterpret_cast<socket::SockAddr *>(std::addressof(addr)), sizeof(addr));
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
s32 Listen(s32 desc, s32 backlog_count) {
switch (g_socket_mode) {
case SocketMode_Htcs: return htcs::Listen(desc, backlog_count);
case SocketMode_Tcp: return socket::Listen(desc, backlog_count);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
s32 Accept(s32 desc) {
switch (g_socket_mode) {
case SocketMode_Htcs:
{
htcs::SockAddrHtcs addr;
addr.family = htcs::HTCS_AF_HTCS;
addr.peer_name = htcs::GetPeerNameAny();
addr.port_name.name[0] = '\x00';
return htcs::Accept(desc, std::addressof(addr));
}
break;
case SocketMode_Tcp:
{
socket::SockAddrIn addr = {};
socket::SockLenT addr_len = sizeof(addr);
return socket::Accept(desc, reinterpret_cast<socket::SockAddr *>(std::addressof(addr)), std::addressof(addr_len));
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
s32 Shutdown(s32 desc) {
switch (g_socket_mode) {
case SocketMode_Htcs: return htcs::Shutdown(desc, htcs::HTCS_SHUT_RDWR);
case SocketMode_Tcp: return socket::Shutdown(desc, socket::ShutdownMethod::Shut_RdWr);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, s32 flags) {
switch (g_socket_mode) {
case SocketMode_Htcs: return htcs::Recv(desc, buffer, buffer_size, flags);
case SocketMode_Tcp: return socket::Recv(desc, buffer, buffer_size, static_cast<socket::MsgFlag>(flags));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, s32 flags) {
switch (g_socket_mode) {
case SocketMode_Htcs: return htcs::Send(desc, buffer, buffer_size, flags);
case SocketMode_Tcp: return socket::Send(desc, buffer, buffer_size, static_cast<socket::MsgFlag>(flags));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
s32 GetLastError() {
switch (g_socket_mode) {
case SocketMode_Htcs: return htcs::GetLastError();
case SocketMode_Tcp: return static_cast<s32>(socket::GetLastError());
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
bool IsLastErrorEAgain() {
switch (g_socket_mode) {
case SocketMode_Htcs: return htcs::GetLastError() == htcs::HTCS_EAGAIN;
case SocketMode_Tcp: return socket::GetLastError() == socket::Errno::EAgain;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::dmnt::transport {
void InitializeByHtcs();
void InitializeByTcp();
enum PortName {
PortName_GdbServer,
PortName_GdbDebugLog,
};
s32 Socket();
s32 Close(s32 desc);
s32 Bind(s32 desc, PortName port_name);
s32 Listen(s32 desc, s32 backlog_count);
s32 Accept(s32 desc);
s32 Shutdown(s32 desc);
ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, s32 flags);
ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, s32 flags);
s32 GetLastError();
bool IsLastErrorEAgain();
}

View File

@@ -14,11 +14,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "dmnt2_htcs_receive_buffer.hpp"
#include "dmnt2_transport_receive_buffer.hpp"
namespace ams::dmnt {
ssize_t HtcsReceiveBuffer::Read(void *dst, size_t size) {
ssize_t TransportReceiveBuffer::Read(void *dst, size_t size) {
/* Acquire exclusive access to ourselves. */
std::scoped_lock lk(m_mutex);
@@ -50,7 +50,7 @@ namespace ams::dmnt {
return readable;
}
ssize_t HtcsReceiveBuffer::Write(const void *src, size_t size) {
ssize_t TransportReceiveBuffer::Write(const void *src, size_t size) {
/* Acquire exclusive access to ourselves. */
std::scoped_lock lk(m_mutex);
@@ -71,7 +71,7 @@ namespace ams::dmnt {
return size;
}
bool HtcsReceiveBuffer::WaitToBeReadable() {
bool TransportReceiveBuffer::WaitToBeReadable() {
/* Check if we're already readable. */
{
std::scoped_lock lk(m_mutex);
@@ -91,7 +91,7 @@ namespace ams::dmnt {
return this->IsValid();
}
bool HtcsReceiveBuffer::WaitToBeReadable(TimeSpan timeout) {
bool TransportReceiveBuffer::WaitToBeReadable(TimeSpan timeout) {
/* Check if we're already readable. */
{
std::scoped_lock lk(m_mutex);
@@ -111,7 +111,7 @@ namespace ams::dmnt {
return res && this->IsValid();
}
bool HtcsReceiveBuffer::WaitToBeWritable() {
bool TransportReceiveBuffer::WaitToBeWritable() {
/* Check if we're already writable. */
{
std::scoped_lock lk(m_mutex);
@@ -131,7 +131,7 @@ namespace ams::dmnt {
return this->IsValid();
}
void HtcsReceiveBuffer::Invalidate() {
void TransportReceiveBuffer::Invalidate() {
/* Acquire exclusive access to ourselves. */
std::scoped_lock lk(m_mutex);

View File

@@ -18,7 +18,7 @@
namespace ams::dmnt {
class HtcsReceiveBuffer {
class TransportReceiveBuffer {
public:
static constexpr size_t ReceiveBufferSize = 4_KB;
private:
@@ -30,7 +30,7 @@ namespace ams::dmnt {
size_t m_offset;
bool m_valid;
public:
HtcsReceiveBuffer() : m_readable_event(os::EventClearMode_ManualClear), m_writable_event(os::EventClearMode_ManualClear), m_mutex(), m_readable_size(), m_offset(), m_valid(true) { /* ... */ }
TransportReceiveBuffer() : m_readable_event(os::EventClearMode_ManualClear), m_writable_event(os::EventClearMode_ManualClear), m_mutex(), m_readable_size(), m_offset(), m_valid(true) { /* ... */ }
ALWAYS_INLINE bool IsReadable() const { return m_readable_size != 0; }
ALWAYS_INLINE bool IsWritable() const { return m_readable_size == 0; }

View File

@@ -14,12 +14,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "dmnt2_htcs_session.hpp"
#include "dmnt2_transport_layer.hpp"
#include "dmnt2_transport_session.hpp"
#include "dmnt2_debug_log.hpp"
namespace ams::dmnt {
HtcsSession::HtcsSession(int fd) : m_socket(fd), m_valid(true) {
TransportSession::TransportSession(int fd) : m_socket(fd), m_valid(true) {
/* Create our thread. */
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_receive_thread), ReceiveThreadEntry, this, m_receive_thread_stack, sizeof(m_receive_thread_stack), os::HighestThreadPriority - 1));
@@ -30,7 +31,7 @@ namespace ams::dmnt {
AMS_DMNT2_GDB_LOG_INFO("Created Session %d\n", m_socket);
}
HtcsSession::~HtcsSession() {
TransportSession::~TransportSession() {
/* Note that we connected. */
AMS_DMNT2_GDB_LOG_INFO("Closing Session %d\n", m_socket);
@@ -38,25 +39,25 @@ namespace ams::dmnt {
m_receive_buffer.Invalidate();
/* Shutdown our socket. */
htcs::Shutdown(m_socket, htcs::HTCS_SHUT_RDWR);
transport::Shutdown(m_socket);
/* Wait for our thread. */
os::WaitThread(std::addressof(m_receive_thread));
os::DestroyThread(std::addressof(m_receive_thread));
/* Close our socket. */
htcs::Close(m_socket);
transport::Close(m_socket);
}
bool HtcsSession::WaitToBeReadable() {
bool TransportSession::WaitToBeReadable() {
return m_receive_buffer.WaitToBeReadable();
}
bool HtcsSession::WaitToBeReadable(TimeSpan timeout) {
bool TransportSession::WaitToBeReadable(TimeSpan timeout) {
return m_receive_buffer.WaitToBeReadable(timeout);
}
util::optional<char> HtcsSession::GetChar() {
util::optional<char> TransportSession::GetChar() {
/* Wait for us to have data. */
m_receive_buffer.WaitToBeReadable();
@@ -69,9 +70,9 @@ namespace ams::dmnt {
}
}
ssize_t HtcsSession::PutChar(char c) {
ssize_t TransportSession::PutChar(char c) {
/* Send the character. */
const auto sent = htcs::Send(m_socket, std::addressof(c), sizeof(c), 0);
const auto sent = transport::Send(m_socket, std::addressof(c), sizeof(c), 0);
if (sent < 0) {
m_valid = false;
}
@@ -79,13 +80,13 @@ namespace ams::dmnt {
return sent;
}
ssize_t HtcsSession::PutString(const char *str) {
ssize_t TransportSession::PutString(const char *str) {
/* Repeatedly send until all is sent. */
const size_t len = std::strlen(str);
size_t remaining = len;
while (remaining > 0) {
const auto sent = htcs::Send(m_socket, str, remaining, 0);
const auto sent = transport::Send(m_socket, str, remaining, 0);
if (sent >= 0) {
remaining -= sent;
str += sent;
@@ -98,22 +99,22 @@ namespace ams::dmnt {
return len;
}
void HtcsSession::ReceiveThreadFunction() {
void TransportSession::ReceiveThreadFunction() {
/* Create temporary buffer. */
u8 buffer[HtcsReceiveBuffer::ReceiveBufferSize];
u8 buffer[TransportReceiveBuffer::ReceiveBufferSize];
/* Loop receiving data. */
while (true) {
/* Receive data. */
const auto res = htcs::Recv(m_socket, buffer, sizeof(buffer), 0);
const auto res = transport::Recv(m_socket, buffer, sizeof(buffer), 0);
if (res > 0) {
/* Write the data to our buffer. */
m_receive_buffer.WaitToBeWritable();
m_receive_buffer.Write(buffer, res);
} else {
/* Otherwise, if we got an error other than "try again", we're done. */
if (htcs::GetLastError() != htcs::HTCS_EAGAIN) {
AMS_DMNT2_GDB_LOG_INFO("Session %d invalid, res=%ld, err=%d\n", m_socket, res, static_cast<int>(htcs::GetLastError()));
if (!transport::IsLastErrorEAgain()) {
AMS_DMNT2_GDB_LOG_INFO("Session %d invalid, res=%ld, err=%d\n", m_socket, res, static_cast<int>(transport::GetLastError()));
m_valid = false;
break;
}

View File

@@ -15,20 +15,20 @@
*/
#pragma once
#include <stratosphere.hpp>
#include "dmnt2_htcs_receive_buffer.hpp"
#include "dmnt2_transport_receive_buffer.hpp"
namespace ams::dmnt {
class HtcsSession {
class TransportSession {
private:
alignas(os::ThreadStackAlignment) u8 m_receive_thread_stack[util::AlignUp(os::MemoryPageSize + HtcsReceiveBuffer::ReceiveBufferSize, os::ThreadStackAlignment)];
HtcsReceiveBuffer m_receive_buffer;
alignas(os::ThreadStackAlignment) u8 m_receive_thread_stack[util::AlignUp(os::MemoryPageSize + TransportReceiveBuffer::ReceiveBufferSize, os::ThreadStackAlignment)];
TransportReceiveBuffer m_receive_buffer;
os::ThreadType m_receive_thread;
int m_socket;
bool m_valid;
public:
HtcsSession(int fd);
~HtcsSession();
TransportSession(int fd);
~TransportSession();
ALWAYS_INLINE bool IsValid() const { return m_valid; }
@@ -41,7 +41,7 @@ namespace ams::dmnt {
private:
static void ReceiveThreadEntry(void *arg) {
static_cast<HtcsSession *>(arg)->ReceiveThreadFunction();
static_cast<TransportSession *>(arg)->ReceiveThreadFunction();
}
void ReceiveThreadFunction();

View File

@@ -248,7 +248,6 @@ namespace ams::dmnt::cheat::impl {
void StartProcess(os::ProcessId process_id) const {
R_ABORT_UNLESS(pm::dmnt::StartProcess(process_id));
}
public:
CheatProcessManager() : m_cheat_lock(), m_unsafe_break_event(os::EventClearMode_ManualClear), m_debug_events_event(os::EventClearMode_AutoClear), m_cheat_process_event(os::EventClearMode_AutoClear, true) {
/* Learn whether we should enable cheats by default. */

View File

@@ -131,11 +131,17 @@ namespace ams::spl {
}
Result SecureMonitorManager::AllocateAesKeySlot(s32 *out_keyslot, const void *owner) {
/* Allocate a new virtual keyslot. */
s32 keyslot;
R_TRY(impl::AllocateAesKeySlot(std::addressof(keyslot)));
/* Get the keyslot's index. */
s32 index;
R_ABORT_UNLESS(impl::TestAesKeySlot(std::addressof(index), keyslot));
bool virt;
R_ABORT_UNLESS(impl::TestAesKeySlot(std::addressof(index), std::addressof(virt), keyslot));
/* All allocated keyslots must be virtual. */
AMS_ABORT_UNLESS(virt);
m_aes_keyslot_owners[index] = owner;
*out_keyslot = keyslot;
@@ -174,10 +180,11 @@ namespace ams::spl {
Result SecureMonitorManager::TestAesKeySlot(s32 *out_index, s32 keyslot, const void *owner) {
/* Validate the keyslot (and get the index). */
s32 index;
R_TRY(impl::TestAesKeySlot(std::addressof(index), keyslot));
bool virt;
R_TRY(impl::TestAesKeySlot(std::addressof(index), std::addressof(virt), keyslot));
/* Check that the keyslot is owned by the request maker. */
R_UNLESS(m_aes_keyslot_owners[index] == owner, spl::ResultInvalidKeySlot());
/* Check that the keyslot is physical (for legacy compat) or owned by the request maker. */
R_UNLESS(!virt || m_aes_keyslot_owners[index] == owner, spl::ResultInvalidKeySlot());
/* Set output index. */
if (out_index != nullptr) {