Compare commits
18 Commits
1.2.2
...
allow_stan
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51d692dfba | ||
|
|
545ade835d | ||
|
|
be5cd35ef6 | ||
|
|
d30af4763a | ||
|
|
7e110ee8f7 | ||
|
|
ea61ac9ea6 | ||
|
|
95ef9da873 | ||
|
|
d2b024f19f | ||
|
|
9011384dbe | ||
|
|
56669e34b7 | ||
|
|
6df7a87727 | ||
|
|
90473a2307 | ||
|
|
29ffa4b7fd | ||
|
|
a73e0e8f16 | ||
|
|
2161365f4f | ||
|
|
258a83684e | ||
|
|
d2a757c39e | ||
|
|
6cf5205a28 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -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
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -112,6 +112,7 @@ dist-no-debug: all
|
||||
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042
|
||||
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/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
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = 4d0f1b79246d7d2c19219bc7da153fdfef165f57
|
||||
parent = 94e18b8c93e6013aaa647d31d54cb3f06e391a46
|
||||
commit = ceff2f3712d66904747f77619cd6dc12a5dcea8b
|
||||
parent = 6cf5205a28cfeb30930fd1cfd3c8369cd13edfa3
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
11
libraries/config/arch/armv4t/arch.mk
Normal file
11
libraries/config/arch/armv4t/arch.mk
Normal file
@@ -0,0 +1,11 @@
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
include $(DEVKITPRO)/devkitARM/base_rules
|
||||
|
||||
export ATMOSPHERE_DEFINES += -DATMOSPHERE_ARCH_ARM_V4T
|
||||
export ATMOSPHERE_SETTINGS +=
|
||||
export ATMOSPHERE_CFLAGS +=
|
||||
export ATMOSPHERE_CXXFLAGS +=
|
||||
export ATMOSPHERE_ASFLAGS +=
|
||||
11
libraries/config/arch/armv8a/arch.mk
Normal file
11
libraries/config/arch/armv8a/arch.mk
Normal file
@@ -0,0 +1,11 @@
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
include $(DEVKITPRO)/devkitA64/base_rules
|
||||
|
||||
export ATMOSPHERE_DEFINES += -DATMOSPHERE_ARCH_ARM_V8A
|
||||
export ATMOSPHERE_SETTINGS +=
|
||||
export ATMOSPHERE_CFLAGS +=
|
||||
export ATMOSPHERE_CXXFLAGS +=
|
||||
export ATMOSPHERE_ASFLAGS +=
|
||||
@@ -39,6 +39,9 @@ export ATMOSPHERE_ARCH_NAME := arm64
|
||||
export ATMOSPHERE_BOARD_NAME := nintendo_nx
|
||||
export ATMOSPHERE_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))
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -57,4 +57,42 @@ namespace ams::socket {
|
||||
}
|
||||
};
|
||||
|
||||
class SystemConfigLightDefault : public Config {
|
||||
public:
|
||||
static constexpr size_t DefaultTcpInitialSendBufferSize = 16_KB;
|
||||
static constexpr size_t DefaultTcpInitialReceiveBufferSize = 16_KB;
|
||||
static constexpr size_t DefaultTcpAutoSendBufferSizeMax = 0_KB;
|
||||
static constexpr size_t DefaultTcpAutoReceiveBufferSizeMax = 0_KB;
|
||||
static constexpr size_t DefaultUdpSendBufferSize = 9_KB;
|
||||
static constexpr size_t DefaultUdpReceiveBufferSize = 42240;
|
||||
static constexpr auto DefaultSocketBufferEfficiency = 2;
|
||||
static constexpr auto DefaultConcurrency = 2;
|
||||
static constexpr size_t DefaultAllocatorPoolSize = 64_KB;
|
||||
|
||||
static constexpr size_t PerTcpSocketWorstCaseMemoryPoolSize = [] {
|
||||
constexpr size_t WorstCaseTcpSendBufferSize = AlignMss(std::max(DefaultTcpInitialSendBufferSize, DefaultTcpAutoSendBufferSizeMax));
|
||||
constexpr size_t WorstCaseTcpReceiveBufferSize = AlignMss(std::max(DefaultTcpInitialReceiveBufferSize, DefaultTcpAutoReceiveBufferSizeMax));
|
||||
|
||||
return util::AlignUp(WorstCaseTcpSendBufferSize * DefaultSocketBufferEfficiency + WorstCaseTcpReceiveBufferSize * DefaultSocketBufferEfficiency, os::MemoryPageSize);
|
||||
}();
|
||||
|
||||
static constexpr size_t PerUdpSocketWorstCaseMemoryPoolSize = [] {
|
||||
constexpr size_t WorstCaseUdpSendBufferSize = AlignMss(DefaultUdpSendBufferSize);
|
||||
constexpr size_t WorstCaseUdpReceiveBufferSize = AlignMss(DefaultUdpReceiveBufferSize);
|
||||
|
||||
return util::AlignUp(WorstCaseUdpSendBufferSize * DefaultSocketBufferEfficiency + WorstCaseUdpReceiveBufferSize * DefaultSocketBufferEfficiency, os::MemoryPageSize);
|
||||
}();
|
||||
public:
|
||||
constexpr SystemConfigLightDefault(void *mp, size_t mp_sz, size_t ap, int c=DefaultConcurrency)
|
||||
: Config(mp, mp_sz, ap,
|
||||
DefaultTcpInitialSendBufferSize, DefaultTcpInitialReceiveBufferSize,
|
||||
DefaultTcpAutoSendBufferSizeMax, DefaultTcpAutoReceiveBufferSizeMax,
|
||||
DefaultUdpSendBufferSize, DefaultUdpReceiveBufferSize,
|
||||
DefaultSocketBufferEfficiency, c)
|
||||
{
|
||||
/* Mark as system. */
|
||||
m_system = true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace ams::spl::impl {
|
||||
|
||||
Result AllocateAesKeySlot(s32 *out_keyslot);
|
||||
Result DeallocateAesKeySlot(s32 keyslot);
|
||||
Result TestAesKeySlot(s32 *out_index, s32 keyslot);
|
||||
Result TestAesKeySlot(s32 *out_index, bool *out_virtual, s32 keyslot);
|
||||
|
||||
os::SystemEvent *GetAesKeySlotAvailableEvent();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 ¤t_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);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
196
stratosphere/dmnt.gen2/source/dmnt2_transport_layer.cpp
Normal file
196
stratosphere/dmnt.gen2/source/dmnt2_transport_layer.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
42
stratosphere/dmnt.gen2/source/dmnt2_transport_layer.hpp
Normal file
42
stratosphere/dmnt.gen2/source/dmnt2_transport_layer.hpp
Normal 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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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; }
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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. */
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user