Compare commits
28 Commits
1.2.1
...
allow_stan
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51d692dfba | ||
|
|
545ade835d | ||
|
|
be5cd35ef6 | ||
|
|
d30af4763a | ||
|
|
7e110ee8f7 | ||
|
|
ea61ac9ea6 | ||
|
|
95ef9da873 | ||
|
|
d2b024f19f | ||
|
|
9011384dbe | ||
|
|
56669e34b7 | ||
|
|
6df7a87727 | ||
|
|
90473a2307 | ||
|
|
29ffa4b7fd | ||
|
|
a73e0e8f16 | ||
|
|
2161365f4f | ||
|
|
258a83684e | ||
|
|
d2a757c39e | ||
|
|
6cf5205a28 | ||
|
|
b9c90b9234 | ||
|
|
94e18b8c93 | ||
|
|
4e92687cab | ||
|
|
2a0b99d9f9 | ||
|
|
d1f3c4904b | ||
|
|
92321ccbc8 | ||
|
|
db3004e844 | ||
|
|
3e97e4addf | ||
|
|
4b7b33809f | ||
|
|
e81a1ce5a8 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -91,7 +91,11 @@ dkms.conf
|
||||
**/out
|
||||
**/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
|
||||
|
||||
@@ -1,4 +1,20 @@
|
||||
# Changelog
|
||||
## 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.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = 13c6987cc43d0b9a874e87e3ce8eaffd1c72fa69
|
||||
parent = cb38b0b929f332a52ceeee5c27cb0fd83ff03433
|
||||
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))
|
||||
|
||||
@@ -93,3 +93,4 @@
|
||||
|
||||
/* Deferred includes. */
|
||||
#include <mesosphere/kern_k_auto_object_impls.hpp>
|
||||
#include <mesosphere/kern_k_scheduler_impls.hpp>
|
||||
|
||||
@@ -279,7 +279,7 @@ namespace ams::kern::arch::arm64::init {
|
||||
|
||||
/* Invalidate the entire tlb. */
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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) { /* ... */ }
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <mesosphere/kern_common.hpp>
|
||||
#include <mesosphere/kern_k_scheduler.hpp>
|
||||
#include <mesosphere/kern_select_interrupt_manager.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
/* NOTE: This header is included after all main headers. */
|
||||
consteval bool KScheduler::ValidateAssemblyOffsets() {
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.needs_scheduling) == KSCHEDULER_NEEDS_SCHEDULING);
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.interrupt_task_runnable) == KSCHEDULER_INTERRUPT_TASK_RUNNABLE);
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.highest_priority_thread) == KSCHEDULER_HIGHEST_PRIORITY_THREAD);
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.idle_thread_stack) == KSCHEDULER_IDLE_THREAD_STACK);
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.prev_thread) == KSCHEDULER_PREVIOUS_THREAD);
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.interrupt_task_manager) == KSCHEDULER_INTERRUPT_TASK_MANAGER);
|
||||
|
||||
return true;
|
||||
}
|
||||
static_assert(KScheduler::ValidateAssemblyOffsets());
|
||||
|
||||
ALWAYS_INLINE void KScheduler::RescheduleOtherCores(u64 cores_needing_scheduling) {
|
||||
if (const u64 core_mask = cores_needing_scheduling & ~(1ul << m_core_id); core_mask != 0) {
|
||||
cpu::DataSynchronizationBarrier();
|
||||
Kernel::GetInterruptManager().SendInterProcessorInterrupt(KInterruptName_Scheduler, core_mask);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -405,8 +405,6 @@ namespace ams::kern {
|
||||
constexpr ThreadState GetState() const { return static_cast<ThreadState>(m_thread_state & ThreadState_Mask); }
|
||||
constexpr ThreadState 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:
|
||||
|
||||
@@ -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); }
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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). */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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())));
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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 2
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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