Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"

This reverts commit 15b7df8ef1.
This commit is contained in:
souldbminersmwc
2025-11-09 16:14:52 -05:00
parent 22ec140738
commit 21a3f953d7
3804 changed files with 435 additions and 570162 deletions

View File

@@ -1,89 +0,0 @@
{
"name": "LogManager",
"title_id": "0x0100000000000420",
"title_id_range_min": "0x0100000000000420",
"title_id_range_max": "0x0100000000000420",
"main_thread_stack_size": "0x00003000",
"main_thread_priority": 38,
"default_cpu_id": 3,
"process_category": 0,
"is_retail": true,
"pool_partition": 2,
"is_64_bit": true,
"address_space_type": 1,
"filesystem_access": {
"permissions": "0xFFFFFFFFFFFFFFFF"
},
"service_access": ["fsp-srv", "htc", "htcs", "psc:m", "set", "set:sys", "srepo:u", "time:su", "time:u", "tma_log"],
"service_host": ["lm", "lm:get"],
"kernel_capabilities": [{
"type": "kernel_flags",
"value": {
"highest_thread_priority": 63,
"lowest_thread_priority": 24,
"lowest_cpu_id": 3,
"highest_cpu_id": 3
}
}, {
"type": "syscalls",
"value": {
"svcSetHeapSize": "0x01",
"svcSetMemoryPermission": "0x02",
"svcSetMemoryAttribute": "0x03",
"svcMapMemory": "0x04",
"svcUnmapMemory": "0x05",
"svcQueryMemory": "0x06",
"svcExitProcess": "0x07",
"svcCreateThread": "0x08",
"svcStartThread": "0x09",
"svcExitThread": "0x0a",
"svcSleepThread": "0x0b",
"svcGetThreadPriority": "0x0c",
"svcSetThreadPriority": "0x0d",
"svcGetThreadCoreMask": "0x0e",
"svcSetThreadCoreMask": "0x0f",
"svcGetCurrentProcessorNumber": "0x10",
"svcSignalEvent": "0x11",
"svcClearEvent": "0x12",
"svcMapSharedMemory": "0x13",
"svcUnmapSharedMemory": "0x14",
"svcCreateTransferMemory": "0x15",
"svcCloseHandle": "0x16",
"svcResetSignal": "0x17",
"svcWaitSynchronization": "0x18",
"svcCancelSynchronization": "0x19",
"svcArbitrateLock": "0x1a",
"svcArbitrateUnlock": "0x1b",
"svcWaitProcessWideKeyAtomic": "0x1c",
"svcSignalProcessWideKey": "0x1d",
"svcGetSystemTick": "0x1e",
"svcConnectToNamedPort": "0x1f",
"svcSendSyncRequestLight": "0x20",
"svcSendSyncRequest": "0x21",
"svcSendSyncRequestWithUserBuffer": "0x22",
"svcSendAsyncRequestWithUserBuffer": "0x23",
"svcGetProcessId": "0x24",
"svcGetThreadId": "0x25",
"svcBreak": "0x26",
"svcOutputDebugString": "0x27",
"svcReturnFromException": "0x28",
"svcGetInfo": "0x29",
"svcWaitForAddress": "0x34",
"svcSignalToAddress": "0x35",
"svcSynchronizePreemptionState": "0x36",
"svcCreateSession": "0x40",
"svcAcceptSession": "0x41",
"svcReplyAndReceiveLight": "0x42",
"svcReplyAndReceive": "0x43",
"svcReplyAndReceiveWithUserBuffer": "0x44",
"svcCreateEvent": "0x45",
"svcCallSecureMonitor": "0x7f"
}
}, {
"type": "min_kernel_version",
"value": "0x0030"
}, {
"type": "handle_table_size",
"value": 64
}]
}

View File

@@ -1,41 +0,0 @@
ATMOSPHERE_BUILD_CONFIGS :=
all: nx_release
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
define ATMOSPHERE_ADD_TARGET
ATMOSPHERE_BUILD_CONFIGS += $(strip $1)
$(strip $1):
@echo "Building $(strip $1)"
@$$(MAKE) -f $(CURRENT_DIRECTORY)/system_module.mk ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
clean-$(strip $1):
@echo "Cleaning $(strip $1)"
@$$(MAKE) -f $(CURRENT_DIRECTORY)/system_module.mk clean ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
endef
define ATMOSPHERE_ADD_TARGETS
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_release, release, $(strip $2), $(strip $3), \
ATMOSPHERE_BUILD_SETTINGS="$(strip $4)" \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_debug, debug, $(strip $2), $(strip $3), \
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_DEBUGGING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_audit, audit, $(strip $2), $(strip $3), \
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_AUDITING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 ATMOSPHERE_BUILD_FOR_AUDITING=1 \
))
endef
$(eval $(call ATMOSPHERE_ADD_TARGETS, nx, nx-hac-001, arm-cortex-a57,))
clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config))
.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS), $(config) clean-$(config))

View File

@@ -1,80 +0,0 @@
/*
* 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>
namespace ams {
namespace lm::srv {
void StartLogServerProxy();
void StopLogServerProxy();
void InitializeFlushThread();
void FinalizeFlushThread();
void InitializeIpcServer();
void LoopIpcServer();
void FinalizeIpcServer();
}
namespace init {
void InitializeSystemModule() {
/* Initialize our connection to sm. */
R_ABORT_UNLESS(sm::Initialize());
/* Initialize services we need. */
R_ABORT_UNLESS(::setsysInitialize());
R_ABORT_UNLESS(::pscmInitialize());
/* Verify that we can sanely execute. */
ams::CheckApiVersion();
}
void FinalizeSystemModule() { /* ... */ }
void Startup() { /* ... */ }
}
void Main() {
/* Check thread priority. */
AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(LogManager, MainThread));
/* Set thread name. */
os::ChangeThreadPriority(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_PRIORITY(lm, IpcServer));
os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(lm, IpcServer));
/* Start log server proxy. */
lm::srv::StartLogServerProxy();
/* Initialize flush thread. */
lm::srv::InitializeFlushThread();
/* Process IPC server. */
lm::srv::InitializeIpcServer();
lm::srv::LoopIpcServer();
lm::srv::FinalizeIpcServer();
/* Finalize flush thread. */
lm::srv::FinalizeFlushThread();
/* Stop log server proxy. */
lm::srv::StopLogServerProxy();
}
}

View File

@@ -1,131 +0,0 @@
#---------------------------------------------------------------------------------
# pull in common stratosphere sysmodule configuration
#---------------------------------------------------------------------------------
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk
ATMOSPHERE_SYSTEM_MODULE_TARGETS := nsp
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(__RECURSIVE__),1)
#---------------------------------------------------------------------------------
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c)
CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp)
SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s)
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
$(foreach dir,$(AMS_LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(ATMOSPHERE_BUILD_DIR)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) $(foreach dir,$(AMS_LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
.PHONY: clean all check_lib
#---------------------------------------------------------------------------------
all: $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
@$(MAKE) __RECURSIVE__=1 OUTPUT=$(CURDIR)/$(ATMOSPHERE_OUT_DIR)/$(TARGET) \
DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \
--no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \
-f $(THIS_MAKEFILE)
$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a: check_lib
@$(SILENTCMD)echo "Checked library."
ifeq ($(ATMOSPHERE_CHECKED_LIBSTRATOSPHERE),1)
check_lib:
else
check_lib:
@$(MAKE) --no-print-directory -C $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere -f $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/libstratosphere.mk
endif
$(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR):
@[ -d $@ ] || mkdir -p $@
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR)
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(foreach target,$(ATMOSPHERE_SYSTEM_MODULE_TARGETS),$(OUTPUT).$(target))
$(OUTPUT).kip : $(OUTPUT).elf
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
%.npdm : %.npdm.json
@echo built ... $< $@
@npdmtool $< $@
@echo built ... $(notdir $@)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@@ -1,41 +0,0 @@
ATMOSPHERE_BUILD_CONFIGS :=
all: nx_release
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
define ATMOSPHERE_ADD_TARGET
ATMOSPHERE_BUILD_CONFIGS += $(strip $1)
$(strip $1):
@echo "Building $(strip $1)"
@$$(MAKE) -f $(CURRENT_DIRECTORY)/stratosphere.mk ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
clean-$(strip $1):
@echo "Cleaning $(strip $1)"
@$$(MAKE) -f $(CURRENT_DIRECTORY)/stratosphere.mk clean ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
endef
define ATMOSPHERE_ADD_TARGETS
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_release, release, $(strip $2), $(strip $3), \
ATMOSPHERE_BUILD_SETTINGS="$(strip $4)" \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_debug, debug, $(strip $2), $(strip $3), \
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_DEBUGGING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_audit, audit, $(strip $2), $(strip $3), \
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_AUDITING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 ATMOSPHERE_BUILD_FOR_AUDITING=1 \
))
endef
$(eval $(call ATMOSPHERE_ADD_TARGETS, nx, nx-hac-001, arm-cortex-a57,))
clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config))
.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS), $(config) clean-$(config))

View File

@@ -1,41 +0,0 @@
ATMOSPHERE_BUILD_CONFIGS :=
all: nx_release
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
define ATMOSPHERE_ADD_TARGET
ATMOSPHERE_BUILD_CONFIGS += $(strip $1)
$(strip $1):
@echo "Building $(strip $1)"
@$$(MAKE) -f $(CURRENT_DIRECTORY)/system_module.mk ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
clean-$(strip $1):
@echo "Cleaning $(strip $1)"
@$$(MAKE) -f $(CURRENT_DIRECTORY)/system_module.mk clean ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
endef
define ATMOSPHERE_ADD_TARGETS
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_release, release, $(strip $2), $(strip $3), \
ATMOSPHERE_BUILD_SETTINGS="$(strip $4)" \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_debug, debug, $(strip $2), $(strip $3), \
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_DEBUGGING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_audit, audit, $(strip $2), $(strip $3), \
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_AUDITING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 ATMOSPHERE_BUILD_FOR_AUDITING=1 \
))
endef
$(eval $(call ATMOSPHERE_ADD_TARGETS, nx, nx-hac-001, arm-cortex-a57,))
clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config))
.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS), $(config) clean-$(config))

View File

@@ -1,84 +0,0 @@
{
"name": "TioServer",
"title_id": "0x010000000000D623",
"title_id_range_min": "0x010000000000D623",
"title_id_range_max": "0x010000000000D623",
"main_thread_stack_size": "0x00004000",
"main_thread_priority": 49,
"default_cpu_id": 3,
"process_category": 0,
"is_retail": true,
"pool_partition": 2,
"is_64_bit": true,
"address_space_type": 3,
"disable_device_address_space_merge": true,
"filesystem_access": {
"permissions": "0xFFFFFFFFFFFFFFFF"
},
"service_access": ["fatal:u", "fsp-srv", "htcs"],
"service_host": [],
"kernel_capabilities": [{
"type": "kernel_flags",
"value": {
"highest_thread_priority": 63,
"lowest_thread_priority": 24,
"lowest_cpu_id": 3,
"highest_cpu_id": 3
}
}, {
"type": "syscalls",
"value": {
"svcSetHeapSize": "0x01",
"svcSetMemoryPermission": "0x02",
"svcSetMemoryAttribute": "0x03",
"svcMapMemory": "0x04",
"svcUnmapMemory": "0x05",
"svcQueryMemory": "0x06",
"svcExitProcess": "0x07",
"svcCreateThread": "0x08",
"svcStartThread": "0x09",
"svcExitThread": "0x0a",
"svcSleepThread": "0x0b",
"svcGetThreadPriority": "0x0c",
"svcSetThreadPriority": "0x0d",
"svcGetThreadCoreMask": "0x0e",
"svcSetThreadCoreMask": "0x0f",
"svcGetCurrentProcessorNumber": "0x10",
"svcSignalEvent": "0x11",
"svcClearEvent": "0x12",
"svcMapSharedMemory": "0x13",
"svcUnmapSharedMemory": "0x14",
"svcCreateTransferMemory": "0x15",
"svcCloseHandle": "0x16",
"svcResetSignal": "0x17",
"svcWaitSynchronization": "0x18",
"svcCancelSynchronization": "0x19",
"svcArbitrateLock": "0x1a",
"svcArbitrateUnlock": "0x1b",
"svcWaitProcessWideKeyAtomic": "0x1c",
"svcSignalProcessWideKey": "0x1d",
"svcGetSystemTick": "0x1e",
"svcConnectToNamedPort": "0x1f",
"svcSendSyncRequestLight": "0x20",
"svcSendSyncRequest": "0x21",
"svcSendSyncRequestWithUserBuffer": "0x22",
"svcSendAsyncRequestWithUserBuffer": "0x23",
"svcGetProcessId": "0x24",
"svcGetThreadId": "0x25",
"svcBreak": "0x26",
"svcOutputDebugString": "0x27",
"svcReturnFromException": "0x28",
"svcGetInfo": "0x29",
"svcWaitForAddress": "0x34",
"svcSignalToAddress": "0x35",
"svcSynchronizePreemptionState": "0x36",
"svcCallSecureMonitor": "0x7f"
}
}, {
"type": "min_kernel_version",
"value": "0x0030"
}, {
"type": "handle_table_size",
"value": 0
}]
}

View File

@@ -1,159 +0,0 @@
/*
* 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 "tio_file_server.hpp"
#include "tio_file_server_packet.hpp"
#include "tio_file_server_htcs_server.hpp"
#include "tio_file_server_processor.hpp"
#include "tio_sd_card_observer.hpp"
namespace ams::tio {
namespace {
constexpr inline auto NumDispatchThreads = 2;
constexpr inline auto DispatchThreadPriority = 21;
constexpr inline size_t RequestBufferSize = 1_MB + util::AlignUp(0x40 + fs::EntryNameLengthMax, 1_KB);
struct FileServerRequest {
int socket;
FileServerRequestHeader header;
u8 body[RequestBufferSize];
};
constexpr const char HtcsPortName[] = "iywys@$TioServer_FileServer";
alignas(os::ThreadStackAlignment) u8 g_server_stack[os::MemoryPageSize];
alignas(os::ThreadStackAlignment) u8 g_observer_stack[os::MemoryPageSize];
alignas(os::ThreadStackAlignment) u8 g_dispatch_stacks[NumDispatchThreads][os::MemoryPageSize];
constinit FileServerHtcsServer g_file_server_htcs_server;
constinit FileServerProcessor g_file_server_processor(g_file_server_htcs_server);
constinit SdCardObserver g_sd_card_observer;
constinit os::ThreadType g_file_server_dispatch_threads[NumDispatchThreads];
constinit FileServerRequest g_requests[NumDispatchThreads];
constinit os::MessageQueueType g_free_mq;
constinit os::MessageQueueType g_dispatch_mq;
constinit uintptr_t g_free_mq_storage[NumDispatchThreads];
constinit uintptr_t g_dispatch_mq_storage[NumDispatchThreads];
void OnSdCardInsertionChanged(bool inserted) {
g_file_server_processor.SetInserted(inserted);
}
void OnFileServerHtcsSocketAccepted(int fd) {
/* Service requests, while we can. */
while (true) {
/* Receive a free request. */
uintptr_t request_address;
os::ReceiveMessageQueue(std::addressof(request_address), std::addressof(g_free_mq));
/* Ensure we manage our request properly. */
auto req_guard = SCOPE_GUARD { os::SendMessageQueue(std::addressof(g_free_mq), request_address); };
/* Receive the request header. */
FileServerRequest *request = reinterpret_cast<FileServerRequest *>(request_address);
if (htcs::Recv(fd, std::addressof(request->header), sizeof(request->header), htcs::HTCS_MSG_WAITALL) != sizeof(request->header)) {
break;
}
/* Receive the request body, if necessary. */
if (request->header.body_size > 0) {
if (htcs::Recv(fd, request->body, request->header.body_size, htcs::HTCS_MSG_WAITALL) != request->header.body_size) {
break;
}
}
/* Dispatch the request. */
req_guard.Cancel();
request->socket = fd;
os::SendMessageQueue(std::addressof(g_dispatch_mq), request_address);
}
/* Our socket is no longer making requests, so close it. */
htcs::Close(fd);
/* Clean up any server resources. */
g_file_server_processor.Unmount();
}
void FileServerDispatchThreadFunction(void *) {
while (true) {
/* Receive a request. */
uintptr_t request_address;
os::ReceiveMessageQueue(std::addressof(request_address), std::addressof(g_dispatch_mq));
/* Process the request. */
FileServerRequest *request = reinterpret_cast<FileServerRequest *>(request_address);
if (!g_file_server_processor.ProcessRequest(std::addressof(request->header), request->body, request->socket)) {
htcs::Close(request->socket);
}
/* Free the request. */
os::SendMessageQueue(std::addressof(g_free_mq), request_address);
}
}
}
void InitializeFileServer() {
/* Initialize the htcs server. */
g_file_server_htcs_server.Initialize(HtcsPortName, g_server_stack, sizeof(g_server_stack), OnFileServerHtcsSocketAccepted);
/* Initialize SD card observer. */
g_sd_card_observer.Initialize(g_observer_stack, sizeof(g_observer_stack));
g_sd_card_observer.SetCallback(OnSdCardInsertionChanged);
/* Initialize the command processor. */
g_file_server_processor.SetInserted(g_sd_card_observer.IsSdCardInserted());
g_file_server_processor.SetRequestBufferSize(RequestBufferSize);
/* Initialize the dispatch message queues. */
os::InitializeMessageQueue(std::addressof(g_free_mq), g_free_mq_storage, util::size(g_free_mq_storage));
os::InitializeMessageQueue(std::addressof(g_dispatch_mq), g_dispatch_mq_storage, util::size(g_dispatch_mq_storage));
/* Begin with all requests free. */
for (auto i = 0; i < NumDispatchThreads; ++i) {
os::SendMessageQueue(std::addressof(g_free_mq), reinterpret_cast<uintptr_t>(g_requests + i));
}
/* Initialize the dispatch threads. */
/* NOTE: Nintendo does not name these threads. */
for (auto i = 0; i < NumDispatchThreads; ++i) {
R_ABORT_UNLESS(os::CreateThread(g_file_server_dispatch_threads + i, FileServerDispatchThreadFunction, nullptr, g_dispatch_stacks + i, sizeof(g_dispatch_stacks[i]), DispatchThreadPriority));
}
}
void StartFileServer() {
/* Start the htcs server. */
g_file_server_htcs_server.Start();
/* Start the dispatch threads. */
for (auto i = 0; i < NumDispatchThreads; ++i) {
os::StartThread(g_file_server_dispatch_threads + i);
}
}
void WaitFileServer() {
/* Wait for the htcs server to finish. */
g_file_server_htcs_server.Wait();
}
}

View File

@@ -1,25 +0,0 @@
/*
* 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::tio {
void InitializeFileServer();
void StartFileServer();
void WaitFileServer();
}

View File

@@ -1,94 +0,0 @@
/*
* 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 "tio_file_server_htcs_server.hpp"
namespace ams::tio {
void FileServerHtcsServer::Initialize(const char *port_name, void *thread_stack, size_t thread_stack_size, SocketAcceptedCallback on_socket_accepted) {
/* Set our port name. */
std::strcpy(m_port_name.name, port_name);
/* Set our callback. */
m_on_socket_accepted = on_socket_accepted;
/* Setup our thread. */
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_thread), ThreadEntry, this, thread_stack, thread_stack_size, AMS_GET_SYSTEM_THREAD_PRIORITY(TioServer, FileServerHtcsServer)));
/* Set our thread name pointer. */
os::SetThreadNamePointer(std::addressof(m_thread), AMS_GET_SYSTEM_THREAD_NAME(TioServer, FileServerHtcsServer));
}
void FileServerHtcsServer::Start() {
os::StartThread(std::addressof(m_thread));
}
void FileServerHtcsServer::Wait() {
os::WaitThread(std::addressof(m_thread));
}
void FileServerHtcsServer::ThreadFunc() {
/* Loop forever, servicing sockets. */
while (true) {
/* Get a socket. */
int fd;
while ((fd = htcs::Socket()) == -1) {
os::SleepThread(TimeSpan::FromSeconds(1));
}
/* Ensure we cleanup the socket when we're done with it. */
ON_SCOPE_EXIT {
htcs::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();
addr.port_name = m_port_name;
/* Bind. */
if (htcs::Bind(fd, std::addressof(addr)) == -1) {
continue;
}
/* Listen on our port. */
while (htcs::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) {
break;
}
/* Handle the client. */
m_on_socket_accepted(client_fd);
}
/* NOTE: This seems unnecessary (client_fd guaranteed < 0 here), but Nintendo does it. */
htcs::Close(client_fd);
}
}
}
ssize_t FileServerHtcsServer::Send(s32 desc, const void *buffer, size_t buffer_size, s32 flags) {
AMS_ASSERT(m_mutex.IsLockedByCurrentThread());
return htcs::Send(desc, buffer, buffer_size, flags);
}
}

View File

@@ -1,47 +0,0 @@
/*
* 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::tio {
using SocketAcceptedCallback = void(*)(s32 desc);
class FileServerHtcsServer {
private:
SocketAcceptedCallback m_on_socket_accepted;
htcs::HtcsPortName m_port_name;
os::ThreadType m_thread;
os::SdkMutex m_mutex;
public:
constexpr FileServerHtcsServer() : m_on_socket_accepted(nullptr), m_port_name{}, m_thread{}, m_mutex{} { /* ... */ }
private:
static void ThreadEntry(void *arg) {
static_cast<FileServerHtcsServer *>(arg)->ThreadFunc();
}
void ThreadFunc();
public:
os::SdkMutex &GetMutex() { return m_mutex; }
public:
void Initialize(const char *port_name, void *thread_stack, size_t thread_stack_size, SocketAcceptedCallback on_socket_accepted);
void Start();
void Wait();
ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, s32 flags);
};
}

View File

@@ -1,187 +0,0 @@
/*
* 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::tio {
enum class PacketType : u32 {
/* Control commands. */
Connect = 0,
Disconnect = 1,
/* Direct filesystem access. */
CreateDirectory = 500,
DeleteDirectory = 501,
DeleteDirectoryRecursively = 502,
OpenDirectory = 503,
CloseDirectory = 504,
RenameDirectory = 505,
CreateFile = 506,
DeleteFile = 507,
OpenFile = 508,
FlushFile = 509,
CloseFile = 510,
RenameFile = 511,
ReadFile = 512,
WriteFile = 513,
GetEntryType = 514,
ReadDirectory = 515,
GetFileSize = 516,
SetFileSize = 517,
GetTotalSpaceSize = 518,
GetFreeSpaceSize = 519,
/* Utilities. */
Stat = 1000,
ListDirectory = 1001,
};
struct FileServerRequestHeader {
u64 request_id;
PacketType packet_type;
u32 body_size;
};
struct FileServerResponseHeader {
u64 request_id;
Result result;
u32 body_size;
};
static_assert(sizeof(FileServerRequestHeader) == sizeof(FileServerResponseHeader));
struct CreateDirectoryParam {
u32 path_len;
char path[];
};
struct DeleteDirectoryParam {
u32 path_len;
char path[];
};
struct DeleteDirectoryRecursivelyParam {
u32 path_len;
char path[];
};
struct OpenDirectoryParam {
u32 path_len;
fs::OpenDirectoryMode open_mode;
char path[];
};
static_assert(sizeof(OpenDirectoryParam) == 0x8);
struct CloseDirectoryParam {
u64 handle;
};
struct RenameDirectoryParam {
u32 old_len;
u32 new_len;
char data[];
};
struct CreateFileParam {
s64 size;
u32 path_len;
fs::CreateOption option;
char path[];
};
static_assert(sizeof(CreateFileParam) == 0x10);
struct DeleteFileParam {
u32 path_len;
char path[];
};
struct OpenFileParam {
u32 path_len;
fs::OpenMode mode;
char path[];
};
static_assert(sizeof(OpenFileParam) == 0x8);
struct FlushFileParam {
u64 handle;
};
struct CloseFileParam {
u64 handle;
};
struct RenameFileParam {
u32 old_len;
u32 new_len;
char data[];
};
struct ReadFileParam {
u64 handle;
s64 offset;
u64 size;
fs::ReadOption option;
};
static_assert(sizeof(ReadFileParam) == 0x20);
struct WriteFileParam {
u64 handle;
s64 offset;
u64 size;
fs::WriteOption option;
};
static_assert(sizeof(WriteFileParam) == 0x20);
struct GetEntryTypeParam {
u32 path_len;
char path[];
};
struct ReadDirectoryParam {
u64 handle;
s64 count;
};
struct GetFileSizeParam {
u64 handle;
};
struct SetFileSizeParam {
u64 handle;
s64 size;
};
struct GetTotalSpaceSizeParam {
u32 path_len;
char path[];
};
struct GetFreeSpaceSizeParam {
u32 path_len;
char path[];
};
struct StatParam {
u32 path_len;
char path[];
};
struct ListDirectoryParam {
u32 path_len;
char path[];
};
}

View File

@@ -1,735 +0,0 @@
/*
* 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 "tio_file_server_processor.hpp"
namespace ams::tio {
namespace {
constexpr inline int ProtocolVersion = 1;
}
void FileServerProcessor::Unmount() {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Close all our directories. */
if (m_open_directory_count > 0) {
for (size_t i = 0; i < util::size(m_directories); ++i) {
if (m_directories[i].handle != nullptr) {
fs::CloseDirectory(m_directories[i]);
m_directories[i] = {};
--m_open_directory_count;
}
}
}
AMS_ABORT_UNLESS(m_open_directory_count == 0);
/* Close all our files. */
if (m_open_file_count > 0) {
for (size_t i = 0; i < util::size(m_files); ++i) {
if (m_files[i].handle != nullptr) {
fs::CloseFile(m_files[i]);
m_files[i] = {};
--m_open_file_count;
}
}
}
AMS_ABORT_UNLESS(m_open_file_count == 0);
/* If we're mounted, unmount the sd card. */
if (m_is_mounted) {
m_is_mounted = false;
fs::Unmount("sd");
}
}
bool FileServerProcessor::ProcessRequest(FileServerRequestHeader *header, u8 *body, int socket) {
/* Declare a response header for us to use. */
FileServerResponseHeader response_header = {
.request_id = header->request_id,
.result = ResultSuccess(),
.body_size = 0,
};
/* Handle the special control commands. */
if (header->packet_type == PacketType::Connect) {
/* If the SD card isn't already mounted, try to mount it. */
if (!m_is_mounted) {
/* Mount the sd card. */
m_is_mounted = !fs::ResultSdCardAccessFailed::Includes(fs::MountSdCard("sd"));
/* Prepare the response. */
char *response_body = reinterpret_cast<char *>(body);
util::SNPrintf(response_body, 0x100, "{\"bufferSize\":%zu, \"sdcardMounted\":%s, \"sdcardInserted\":%s, \"version\":%d}",
m_request_buffer_size,
m_is_mounted ? "true" : "false",
m_is_inserted ? "true" : "false",
ProtocolVersion);
/* Get the response length. */
response_header.body_size = std::strlen(response_body);
}
return this->SendResponse(response_header, body, socket);
} else if (header->packet_type == PacketType::Disconnect) {
/* If we need to, unmount the sd card. */
if (m_is_mounted) {
this->Unmount();
}
/* Send the response. */
return this->SendResponse(response_header, body, socket);
}
/* The SD card must be inserted and mounted for us to process requests. */
if (m_is_inserted && m_is_mounted) {
switch (header->packet_type) {
case PacketType::CreateDirectory:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const CreateDirectoryParam *>(body);
if (header->body_size != sizeof(*param) + param->path_len) {
return false;
}
/* Create the directory. */
response_header.result = fs::CreateDirectory(param->path);
}
break;
case PacketType::DeleteDirectory:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const DeleteDirectoryParam *>(body);
if (header->body_size != sizeof(*param) + param->path_len) {
return false;
}
/* Delete the directory. */
response_header.result = fs::DeleteDirectory(param->path);
}
break;
case PacketType::DeleteDirectoryRecursively:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const DeleteDirectoryRecursivelyParam *>(body);
if (header->body_size != sizeof(*param) + param->path_len) {
return false;
}
/* Delete the directory. */
response_header.result = fs::DeleteDirectoryRecursively(param->path);
}
break;
case PacketType::OpenDirectory:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const OpenDirectoryParam *>(body);
if (header->body_size != sizeof(*param) + param->path_len) {
return false;
}
/* Open the directory. */
fs::DirectoryHandle handle;
response_header.result = fs::OpenDirectory(std::addressof(handle), param->path, param->open_mode);
if (R_SUCCEEDED(response_header.result)) {
std::scoped_lock lk(m_mutex);
if (m_open_directory_count < util::size(m_directories)) {
/* Insert the directory into our table. */
u64 index = std::numeric_limits<u64>::max();
for (size_t i = 0; i < util::size(m_directories); ++i) {
if (m_directories[i].handle == nullptr) {
m_directories[i] = handle;
index = i;
++m_open_directory_count;
break;
}
}
AMS_ABORT_UNLESS(index < util::size(m_directories));
/* Return the index. */
response_header.body_size = sizeof(index);
std::memcpy(body, std::addressof(index), sizeof(index));
} else {
/* We can't actually open the directory. */
fs::CloseDirectory(handle);
response_header.result = fs::ResultOpenCountLimit();
}
}
}
break;
case PacketType::CloseDirectory:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const CloseDirectoryParam *>(body);
if (header->body_size != sizeof(*param)) {
return false;
}
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that the directory handle is valid. */
if (param->handle >= util::size(m_directories) || m_directories[param->handle].handle == nullptr) {
response_header.result = fs::ResultDataCorrupted();
break;
}
/* Lock the filesystem. */
std::scoped_lock lk2(m_fs_mutex);
/* Close the directory. */
fs::CloseDirectory(m_directories[param->handle]);
m_directories[param->handle].handle = {};
--m_open_directory_count;
}
break;
case PacketType::RenameDirectory:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const RenameDirectoryParam *>(body);
if (header->body_size != sizeof(*param) + param->old_len + param->new_len) {
return false;
}
/* Delete the directory. */
const char *old_path = param->data + 0;
const char *new_path = param->data + param->old_len;
response_header.result = fs::RenameDirectory(old_path, new_path);
}
break;
case PacketType::CreateFile:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const CreateFileParam *>(body);
if (header->body_size != sizeof(*param) + param->path_len) {
return false;
}
/* Create the file. */
response_header.result = fs::CreateFile(param->path, param->size, static_cast<int>(param->option));
}
break;
case PacketType::DeleteFile:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const DeleteFileParam *>(body);
if (header->body_size != sizeof(*param) + param->path_len) {
return false;
}
/* Delete the file. */
response_header.result = fs::DeleteFile(param->path);
}
break;
case PacketType::OpenFile:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const OpenFileParam *>(body);
if (header->body_size != sizeof(*param) + param->path_len) {
return false;
}
/* Open the file. */
fs::FileHandle handle;
response_header.result = fs::OpenFile(std::addressof(handle), param->path, param->mode);
if (R_SUCCEEDED(response_header.result)) {
std::scoped_lock lk(m_mutex);
if (m_open_file_count < util::size(m_files)) {
/* Insert the file into our table. */
u64 index = std::numeric_limits<u64>::max();
for (size_t i = 0; i < util::size(m_files); ++i) {
if (m_files[i].handle == nullptr) {
m_files[i] = handle;
index = i;
++m_open_file_count;
break;
}
}
AMS_ABORT_UNLESS(index < util::size(m_files));
/* Return the index. */
response_header.body_size = sizeof(index);
std::memcpy(body, std::addressof(index), sizeof(index));
} else {
/* We can't actually open the file. */
fs::CloseFile(handle);
response_header.result = fs::ResultOpenCountLimit();
}
}
}
break;
case PacketType::FlushFile:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const FlushFileParam *>(body);
if (header->body_size != sizeof(*param)) {
return false;
}
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that the file handle is valid. */
if (param->handle >= util::size(m_files) || m_files[param->handle].handle == nullptr) {
response_header.result = fs::ResultDataCorrupted();
break;
}
/* Lock the filesystem. */
std::scoped_lock lk2(m_fs_mutex);
/* Flush the file. */
response_header.result = fs::FlushFile(m_files[param->handle]);
}
break;
case PacketType::CloseFile:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const CloseFileParam *>(body);
if (header->body_size != sizeof(*param)) {
return false;
}
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that the file handle is valid. */
if (param->handle >= util::size(m_files) || m_files[param->handle].handle == nullptr) {
response_header.result = fs::ResultDataCorrupted();
break;
}
/* Lock the filesystem. */
std::scoped_lock lk2(m_fs_mutex);
/* Close the directory. */
fs::CloseFile(m_files[param->handle]);
m_files[param->handle].handle = {};
--m_open_file_count;
}
break;
case PacketType::RenameFile:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const RenameFileParam *>(body);
if (header->body_size != sizeof(*param) + param->old_len + param->new_len) {
return false;
}
/* Delete the directory. */
const char *old_path = param->data + 0;
const char *new_path = param->data + param->old_len;
response_header.result = fs::RenameFile(old_path, new_path);
}
break;
case PacketType::ReadFile:
{
/* Get the parameters. */
const auto param = *reinterpret_cast<const ReadFileParam *>(body);
if (header->body_size != sizeof(param)) {
return false;
}
/* Check that the read is valid. */
if (param.size + sizeof(u64) > m_request_buffer_size) {
response_header.result = fs::ResultDataCorrupted();
break;
}
/* Prepare response variables. */
u64 *out_size = reinterpret_cast<u64 *>(body);
void *dst = out_size + 1;
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that the file handle is valid. */
if (param.handle >= util::size(m_files) || m_files[param.handle].handle == nullptr) {
response_header.result = fs::ResultDataCorrupted();
break;
}
/* Read the file. */
size_t read_size;
response_header.result = fs::ReadFile(std::addressof(read_size), m_files[param.handle], param.offset, dst, param.size);
if (R_SUCCEEDED(response_header.result)) {
*out_size = read_size;
response_header.body_size = sizeof(u64) + read_size;
}
}
break;
case PacketType::WriteFile:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const WriteFileParam *>(body);
if (header->body_size != sizeof(*param) + param->size) {
return false;
}
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that the file handle is valid. */
if (param->handle >= util::size(m_files) || m_files[param->handle].handle == nullptr) {
response_header.result = fs::ResultDataCorrupted();
break;
}
/* Lock the filesystem. */
std::scoped_lock lk2(m_fs_mutex);
/* Write the file. */
response_header.result = fs::WriteFile(m_files[param->handle], param->offset, body + sizeof(*param), param->size, param->option);
}
break;
case PacketType::GetEntryType:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const GetEntryTypeParam *>(body);
if (header->body_size != sizeof(*param) + param->path_len) {
return false;
}
/* Get the entry type. */
fs::DirectoryEntryType type;
response_header.result = fs::GetEntryType(std::addressof(type), param->path);
if (R_SUCCEEDED(response_header.result)) {
/* Return the type. */
response_header.body_size = sizeof(type);
std::memcpy(body, std::addressof(type), sizeof(type));
static_assert(sizeof(type) == sizeof(u32));
}
}
break;
case PacketType::ReadDirectory:
{
/* Get the parameters. */
const auto param = *reinterpret_cast<const ReadDirectoryParam *>(body);
if (header->body_size != sizeof(param)) {
return false;
}
/* Check that the read is valid. */
if (sizeof(s64) + param.count * sizeof(fs::DirectoryEntry) > m_request_buffer_size) {
response_header.result = fs::ResultDataCorrupted();
break;
}
/* Prepare response variables. */
s64 *out_count = reinterpret_cast<s64 *>(body);
fs::DirectoryEntry *dst = reinterpret_cast<fs::DirectoryEntry *>(out_count + 1);
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that the directory handle is valid. */
if (param.handle >= util::size(m_directories) || m_directories[param.handle].handle == nullptr) {
response_header.result = fs::ResultDataCorrupted();
break;
}
/* Read the directory. */
response_header.result = fs::ReadDirectory(out_count, dst, m_directories[param.handle], param.count);
if (R_SUCCEEDED(response_header.result)) {
response_header.body_size = sizeof(s64) + *out_count * sizeof(fs::DirectoryEntry);
}
}
break;
case PacketType::GetFileSize:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const GetFileSizeParam *>(body);
if (header->body_size != sizeof(*param)) {
return false;
}
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that the file handle is valid. */
if (param->handle >= util::size(m_files) || m_files[param->handle].handle == nullptr) {
response_header.result = fs::ResultDataCorrupted();
break;
}
/* Get the file size. */
response_header.result = fs::GetFileSize(reinterpret_cast<s64 *>(body), m_files[param->handle]);
if (R_SUCCEEDED(response_header.result)) {
response_header.body_size = sizeof(s64);
}
}
break;
case PacketType::SetFileSize:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const SetFileSizeParam *>(body);
if (header->body_size != sizeof(*param)) {
return false;
}
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that the file handle is valid. */
if (param->handle >= util::size(m_files) || m_files[param->handle].handle == nullptr) {
response_header.result = fs::ResultDataCorrupted();
break;
}
/* Lock the filesystem. */
std::scoped_lock lk2(m_fs_mutex);
/* Get the file size. */
response_header.result = fs::SetFileSize(m_files[param->handle], param->size);
}
break;
case PacketType::GetTotalSpaceSize:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const GetTotalSpaceSizeParam *>(body);
if (header->body_size != sizeof(*param) + param->path_len) {
return false;
}
/* Get the total space size. */
s64 size;
response_header.result = fs::GetTotalSpaceSize(std::addressof(size), param->path);
if (R_SUCCEEDED(response_header.result)) {
/* Return the size. */
response_header.body_size = sizeof(size);
std::memcpy(body, std::addressof(size), sizeof(size));
}
}
break;
case PacketType::GetFreeSpaceSize:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const GetFreeSpaceSizeParam *>(body);
if (header->body_size != sizeof(*param) + param->path_len) {
return false;
}
/* Get the free space size. */
s64 size;
response_header.result = fs::GetFreeSpaceSize(std::addressof(size), param->path);
if (R_SUCCEEDED(response_header.result)) {
/* Return the size. */
response_header.body_size = sizeof(size);
std::memcpy(body, std::addressof(size), sizeof(size));
}
}
break;
case PacketType::Stat:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const StatParam *>(body);
if (header->body_size != sizeof(*param) + param->path_len) {
return false;
}
/* Prepare a response stat structure. */
struct {
fs::DirectoryEntryType type;
s64 file_size;
fs::FileTimeStampRaw file_timestamp;
} out = {};
static_assert(sizeof(out) == 0x30);
/* Get the entry type. */
response_header.result = fs::GetEntryType(std::addressof(out.type), param->path);
if (R_FAILED(response_header.result)) {
break;
}
/* If the path is a file, get further information. */
if (out.type == fs::DirectoryEntryType_File) {
/* Try to get the file size. */
{
fs::FileHandle handle;
const auto open_result = fs::OpenFile(std::addressof(handle), param->path, fs::OpenMode_Read);
if (R_SUCCEEDED(open_result)) {
ON_SCOPE_EXIT { fs::CloseFile(handle); };
response_header.result = fs::GetFileSize(std::addressof(out.file_size), handle);
if (R_FAILED(response_header.result)) {
break;
}
} else {
if (fs::ResultTargetLocked::Includes(open_result)) {
out.file_size = 0;
} else {
response_header.result = open_result;
break;
}
}
}
/* Get the file timestamp. */
response_header.result = fs::impl::GetFileTimeStampRawForDebug(std::addressof(out.file_timestamp), param->path);
if (R_FAILED(response_header.result)) {
break;
}
}
/* If we successfully got the stat information, send it as response. */
if (R_SUCCEEDED(response_header.result)) {
response_header.body_size = sizeof(out);
std::memcpy(body, std::addressof(out), sizeof(out));
}
}
break;
case PacketType::ListDirectory:
{
/* Get the parameters. */
const auto *param = reinterpret_cast<const ListDirectoryParam *>(body);
if (header->body_size != sizeof(*param) + param->path_len) {
return false;
}
/* Open the directory. */
fs::DirectoryHandle handle;
response_header.result = fs::OpenDirectory(std::addressof(handle), param->path, fs::OpenDirectoryMode_All);
if (R_FAILED(response_header.result)) {
break;
}
/* When we're done, close the handle. */
ON_SCOPE_EXIT { fs::CloseDirectory(handle); };
/* Get the directory entry count. */
s64 count;
response_header.result = fs::GetDirectoryEntryCount(std::addressof(count), handle);
if (R_FAILED(response_header.result)) {
break;
}
/* Determine whether we can send the response in one go. */
const size_t needed_size = sizeof(s64) + sizeof(u64) + sizeof(fs::DirectoryEntry) * count;
if (needed_size <= m_request_buffer_size) {
/* We can perform the entire read in one send. */
struct {
s64 count;
u64 size;
fs::DirectoryEntry entries[];
} *out = reinterpret_cast<decltype(out)>(body);
s64 read_count;
response_header.result = fs::ReadDirectory(std::addressof(read_count), out->entries, handle, count);
if (R_FAILED(response_header.result)) {
break;
}
/* Set the output. */
out->count = read_count;
out->size = read_count * sizeof(fs::DirectoryEntry);
/* Set the response body size. */
response_header.body_size = sizeof(*out) + out->size;
} else {
/* We have to use multiple sends. */
/* Lock our server. */
std::scoped_lock lk(m_htcs_server.GetMutex());
/* Send the response header. */
response_header.body_size = needed_size;
if (m_htcs_server.Send(socket, std::addressof(header), sizeof(header), 0) != sizeof(header)) {
return false;
}
/* Send the body header. */
struct {
s64 count;
u64 size;
} out = { count, count * sizeof(fs::DirectoryEntry) };
if (m_htcs_server.Send(socket, std::addressof(out), sizeof(out), 0) != sizeof(out)) {
return false;
}
/* Loop sending entries. */
s64 remaining = count;
do {
/* Determine how many entries we can read. */
const s64 cur = std::min<s64>(remaining, static_cast<s64>(m_request_buffer_size / sizeof(fs::DirectoryEntry)));
/* NOTE: Nintendo does not check the output of this call. */
s64 read_count = 0;
fs::ReadDirectory(std::addressof(read_count), reinterpret_cast<fs::DirectoryEntry *>(body), handle, cur);
/* Send the current entries. */
const ssize_t cur_size = read_count * sizeof(fs::DirectoryEntry);
if (m_htcs_server.Send(socket, body, cur_size, 0) != cur_size) {
return false;
}
/* Advance. */
remaining -= read_count;
} while (remaining > 0);
/* We've sent the entirety of our response, so early return. */
return true;
}
}
break;
default:
/* Unsupported packet. */
return false;
}
/* Send the response. */
return this->SendResponse(response_header, body, socket);
} else if (m_is_mounted) {
/* The SD card is mounted but not inserted, so we should unmount it. */
this->Unmount();
}
/* We failed to process the request due to SD card not being inserted or mounted. */
response_header.result = fs::ResultSdCardAccessFailed();
return this->SendResponse(response_header, body, socket);
}
bool FileServerProcessor::SendResponse(const FileServerResponseHeader &header, const void *body, int socket) {
/* Lock our server. */
std::scoped_lock lk(m_htcs_server.GetMutex());
/* Send the response header. */
if (m_htcs_server.Send(socket, std::addressof(header), sizeof(header), 0) != sizeof(header)) {
return false;
}
/* If we don't have a body, we're done. */
if (header.body_size == 0) {
return true;
}
/* Send the body. */
return m_htcs_server.Send(socket, body, header.body_size, 0) == header.body_size;
}
}

View File

@@ -1,48 +0,0 @@
/*
* 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>
#include "tio_file_server_htcs_server.hpp"
#include "tio_file_server_packet.hpp"
namespace ams::tio {
class FileServerProcessor {
private:
bool m_is_inserted{};
bool m_is_mounted{};
size_t m_request_buffer_size{};
FileServerHtcsServer &m_htcs_server;
size_t m_open_file_count{};
size_t m_open_directory_count{};
fs::FileHandle m_files[0x80]{};
fs::DirectoryHandle m_directories[0x80]{};
os::SdkMutex m_fs_mutex{};
os::SdkMutex m_mutex{};
public:
constexpr FileServerProcessor(FileServerHtcsServer &htcs_server) : m_htcs_server(htcs_server) { /* ... */ }
void SetInserted(bool ins) { m_is_inserted = ins; }
void SetRequestBufferSize(size_t size) { m_request_buffer_size = size; }
public:
bool ProcessRequest(FileServerRequestHeader *header, u8 *body, int socket);
void Unmount();
private:
bool SendResponse(const FileServerResponseHeader &header, const void *body, int socket);
};
}

View File

@@ -1,93 +0,0 @@
/*
* 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 "tio_file_server.hpp"
namespace ams {
namespace tio {
namespace {
alignas(0x40) constinit u8 g_fs_heap_buffer[64_KB];
alignas(0x40) constinit u8 g_htcs_buffer[1_KB];
lmem::HeapHandle g_fs_heap_handle;
void *AllocateForFs(size_t size) {
return lmem::AllocateFromExpHeap(g_fs_heap_handle, size);
}
void DeallocateForFs(void *p, size_t size) {
AMS_UNUSED(size);
return lmem::FreeToExpHeap(g_fs_heap_handle, p);
}
void InitializeFsHeap() {
/* Setup fs allocator. */
g_fs_heap_handle = lmem::CreateExpHeap(g_fs_heap_buffer, sizeof(g_fs_heap_buffer), lmem::CreateOption_ThreadSafe);
}
}
}
namespace init {
void InitializeSystemModule() {
/* Initialize heap. */
tio::InitializeFsHeap();
/* Initialize our connection to sm. */
R_ABORT_UNLESS(sm::Initialize());
/* Initialize fs. */
fs::InitializeForSystem();
fs::SetAllocator(tio::AllocateForFs, tio::DeallocateForFs);
fs::SetEnabledAutoAbort(false);
/* Verify that we can sanely execute. */
ams::CheckApiVersion();
}
void FinalizeSystemModule() { /* ... */ }
void Startup() { /* ... */ }
}
void Main() {
/* Set thread name. */
os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(TioServer, Main));
AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(TioServer, Main));
/* Initialize htcs. */
constexpr auto HtcsSocketCountMax = 2;
const size_t buffer_size = htcs::GetWorkingMemorySize(HtcsSocketCountMax);
AMS_ABORT_UNLESS(sizeof(tio::g_htcs_buffer) >= buffer_size);
htcs::InitializeForSystem(tio::g_htcs_buffer, buffer_size, HtcsSocketCountMax);
/* Initialize the file server. */
tio::InitializeFileServer();
/* Start the file server. */
tio::StartFileServer();
/* Wait for the file server to finish. */
tio::WaitFileServer();
}
}

View File

@@ -1,64 +0,0 @@
/*
* 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 "tio_sd_card_observer.hpp"
namespace ams::tio {
void SdCardObserver::Initialize(void *thread_stack, size_t thread_stack_size) {
/* Setup our thread. */
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_thread), ThreadEntry, this, thread_stack, thread_stack_size, AMS_GET_SYSTEM_THREAD_PRIORITY(TioServer, SdCardObserver)));
/* Set our thread name pointer. */
os::SetThreadNamePointer(std::addressof(m_thread), AMS_GET_SYSTEM_THREAD_NAME(TioServer, SdCardObserver));
/* Set our initial insertion state. */
m_inserted = fs::IsSdCardInserted();
}
void SdCardObserver::SetCallback(SdCardInsertionCallback callback) {
/* Check that we don't already have a callback. */
AMS_ABORT_UNLESS(m_callback == nullptr);
/* Set our callback. */
m_callback = callback;
}
void SdCardObserver::ThreadFunc() {
/* Open detection event notifier. */
std::unique_ptr<fs::IEventNotifier> notifier;
R_ABORT_UNLESS(fs::OpenSdCardDetectionEventNotifier(std::addressof(notifier)));
/* Bind the detection event. */
os::SystemEventType event;
R_ABORT_UNLESS(notifier->BindEvent(std::addressof(event), os::EventClearMode_AutoClear));
/* Loop, waiting for insertion events. */
while (true) {
/* Wait for an event. */
os::WaitSystemEvent(std::addressof(event));
/* Update our insertion state. */
m_inserted = fs::IsSdCardInserted();
/* Invoke our callback. */
if (m_callback) {
m_callback(m_inserted);
}
}
}
}

View File

@@ -1,43 +0,0 @@
/*
* 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::tio {
using SdCardInsertionCallback = void(*)(bool inserted);
class SdCardObserver {
private:
bool m_inserted;
SdCardInsertionCallback m_callback;
os::ThreadType m_thread;
public:
constexpr SdCardObserver() : m_inserted(false), m_callback(nullptr), m_thread{} { /* ... */ }
bool IsSdCardInserted() const { return m_inserted; }
private:
static void ThreadEntry(void *arg) {
static_cast<SdCardObserver *>(arg)->ThreadFunc();
}
void ThreadFunc();
public:
void Initialize(void *thread_stack, size_t thread_stack_size);
void SetCallback(SdCardInsertionCallback callback);
};
}

View File

@@ -1,131 +0,0 @@
#---------------------------------------------------------------------------------
# pull in common stratosphere sysmodule configuration
#---------------------------------------------------------------------------------
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk
ATMOSPHERE_SYSTEM_MODULE_TARGETS := nsp
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(__RECURSIVE__),1)
#---------------------------------------------------------------------------------
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c)
CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp)
SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s)
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
$(foreach dir,$(AMS_LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(ATMOSPHERE_BUILD_DIR)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) $(foreach dir,$(AMS_LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
.PHONY: clean all check_lib
#---------------------------------------------------------------------------------
all: $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
@$(MAKE) __RECURSIVE__=1 OUTPUT=$(CURDIR)/$(ATMOSPHERE_OUT_DIR)/$(TARGET) \
DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \
--no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \
-f $(THIS_MAKEFILE)
$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a: check_lib
@$(SILENTCMD)echo "Checked library."
ifeq ($(ATMOSPHERE_CHECKED_LIBSTRATOSPHERE),1)
check_lib:
else
check_lib:
@$(MAKE) --no-print-directory -C $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere -f $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/libstratosphere.mk
endif
$(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR):
@[ -d $@ ] || mkdir -p $@
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR)
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(foreach target,$(ATMOSPHERE_SYSTEM_MODULE_TARGETS),$(OUTPUT).$(target))
$(OUTPUT).kip : $(OUTPUT).elf
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
%.npdm : %.npdm.json
@echo built ... $< $@
@npdmtool $< $@
@echo built ... $(notdir $@)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@@ -1,41 +0,0 @@
ATMOSPHERE_BUILD_CONFIGS :=
all: nx_release
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
define ATMOSPHERE_ADD_TARGET
ATMOSPHERE_BUILD_CONFIGS += $(strip $1)
$(strip $1):
@echo "Building $(strip $1)"
@$$(MAKE) -f $(CURRENT_DIRECTORY)/system_module.mk ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
clean-$(strip $1):
@echo "Cleaning $(strip $1)"
@$$(MAKE) -f $(CURRENT_DIRECTORY)/system_module.mk clean ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
endef
define ATMOSPHERE_ADD_TARGETS
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_release, release, $(strip $2), $(strip $3), \
ATMOSPHERE_BUILD_SETTINGS="$(strip $4)" \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_debug, debug, $(strip $2), $(strip $3), \
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_DEBUGGING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_audit, audit, $(strip $2), $(strip $3), \
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_AUDITING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 ATMOSPHERE_BUILD_FOR_AUDITING=1 \
))
endef
$(eval $(call ATMOSPHERE_ADD_TARGETS, nx, nx-hac-001, arm-cortex-a57,))
clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config))
.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS), $(config) clean-$(config))

View File

@@ -1,86 +0,0 @@
{
"name": "ams.mitm",
"title_id": "0x010041544D530000",
"main_thread_stack_size": "0x20000",
"main_thread_priority": 43,
"default_cpu_id": 3,
"process_category": 1,
"use_secure_memory": true,
"immortal": true,
"kernel_capabilities": [
{
"type": "handle_table_size",
"value": 512
},
{
"type": "syscalls",
"value": {
"svcSetHeapSize": "0x01",
"svcSetMemoryPermission": "0x02",
"svcSetMemoryAttribute": "0x03",
"svcMapMemory": "0x04",
"svcUnmapMemory": "0x05",
"svcQueryMemory": "0x06",
"svcExitProcess": "0x07",
"svcCreateThread": "0x08",
"svcStartThread": "0x09",
"svcExitThread": "0x0a",
"svcSleepThread": "0x0b",
"svcGetThreadPriority": "0x0c",
"svcSetThreadPriority": "0x0d",
"svcGetThreadCoreMask": "0x0e",
"svcSetThreadCoreMask": "0x0f",
"svcGetCurrentProcessorNumber": "0x10",
"svcSignalEvent": "0x11",
"svcClearEvent": "0x12",
"svcMapSharedMemory": "0x13",
"svcUnmapSharedMemory": "0x14",
"svcCreateTransferMemory": "0x15",
"svcCloseHandle": "0x16",
"svcResetSignal": "0x17",
"svcWaitSynchronization": "0x18",
"svcCancelSynchronization": "0x19",
"svcArbitrateLock": "0x1a",
"svcArbitrateUnlock": "0x1b",
"svcWaitProcessWideKeyAtomic": "0x1c",
"svcSignalProcessWideKey": "0x1d",
"svcGetSystemTick": "0x1e",
"svcConnectToNamedPort": "0x1f",
"svcSendSyncRequestLight": "0x20",
"svcSendSyncRequest": "0x21",
"svcSendSyncRequestWithUserBuffer": "0x22",
"svcSendAsyncRequestWithUserBuffer": "0x23",
"svcGetProcessId": "0x24",
"svcGetThreadId": "0x25",
"svcBreak": "0x26",
"svcOutputDebugString": "0x27",
"svcReturnFromException": "0x28",
"svcGetInfo": "0x29",
"svcWaitForAddress": "0x34",
"svcSignalToAddress": "0x35",
"svcSynchronizePreemptionState": "0x36",
"svcCreateSession": "0x40",
"svcAcceptSession": "0x41",
"svcReplyAndReceiveLight": "0x42",
"svcReplyAndReceive": "0x43",
"svcReplyAndReceiveWithUserBuffer": "0x44",
"svcCreateEvent": "0x45",
"svcMapPhysicalMemoryUnsafe": "0x48",
"svcUnmapPhysicalMemoryUnsafe": "0x49",
"svcMapTransferMemory": "0x51",
"svcUnmapTransferMemory": "0x52",
"svcCreateInterruptEvent": "0x53",
"svcReadWriteRegister": "0x4E",
"svcQueryIoMapping": "0x55",
"svcCreateDeviceAddressSpace": "0x56",
"svcAttachDeviceAddressSpace": "0x57",
"svcDetachDeviceAddressSpace": "0x58",
"svcMapDeviceAddressSpaceAligned": "0x5a",
"svcUnmapDeviceAddressSpace": "0x5c",
"svcGetSystemInfo": "0x6f",
"svcManageNamedPort": "0x71",
"svcCallSecureMonitor": "0x7F"
}
}
]
}

View File

@@ -1,261 +0,0 @@
/*
* 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 "amsmitm_initialization.hpp"
#include "amsmitm_fs_utils.hpp"
namespace ams::mitm::fs {
using namespace ams::fs;
namespace {
/* Globals. */
FsFileSystem g_sd_filesystem;
/* Helpers. */
Result EnsureSdInitialized() {
R_UNLESS(serviceIsActive(std::addressof(g_sd_filesystem.s)), ams::fs::ResultSdCardNotPresent());
R_SUCCEED();
}
void FormatAtmosphereRomfsPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *src_path) {
return FormatAtmosphereSdPath(dst_path, dst_path_size, program_id, "romfs", src_path);
}
}
void OpenGlobalSdCardFileSystem() {
R_ABORT_UNLESS(fsOpenSdCardFileSystem(std::addressof(g_sd_filesystem)));
}
Result CreateSdFile(const char *path, s64 size, s32 option) {
R_TRY(EnsureSdInitialized());
R_RETURN(fsFsCreateFile(std::addressof(g_sd_filesystem), path, size, option));
}
Result DeleteSdFile(const char *path) {
R_TRY(EnsureSdInitialized());
R_RETURN(fsFsDeleteFile(std::addressof(g_sd_filesystem), path));
}
bool HasSdFile(const char *path) {
if (R_FAILED(EnsureSdInitialized())) {
return false;
}
FsDirEntryType type;
if (R_FAILED(fsFsGetEntryType(std::addressof(g_sd_filesystem), path, std::addressof(type)))) {
return false;
}
return type == FsDirEntryType_File;
}
bool HasAtmosphereSdFile(const char *path) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
return HasSdFile(fixed_path);
}
Result DeleteAtmosphereSdFile(const char *path) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
R_RETURN(DeleteSdFile(fixed_path));
}
Result CreateAtmosphereSdFile(const char *path, s64 size, s32 option) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
R_RETURN(CreateSdFile(fixed_path, size, option));
}
Result OpenSdFile(FsFile *out, const char *path, u32 mode) {
R_TRY(EnsureSdInitialized());
R_RETURN(fsFsOpenFile(std::addressof(g_sd_filesystem), path, mode, out));
}
Result OpenAtmosphereSdFile(FsFile *out, const char *path, u32 mode) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
R_RETURN(OpenSdFile(out, fixed_path, mode));
}
Result OpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path);
R_RETURN(OpenSdFile(out, fixed_path, mode));
}
Result OpenAtmosphereSdRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
R_RETURN(OpenSdFile(out, fixed_path, mode));
}
Result OpenAtmosphereRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
R_RETURN(fsFsOpenFile(fs, fixed_path, mode, out));
}
Result CreateSdDirectory(const char *path) {
R_TRY(EnsureSdInitialized());
R_RETURN(fsFsCreateDirectory(std::addressof(g_sd_filesystem), path));
}
Result CreateAtmosphereSdDirectory(const char *path) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
R_RETURN(CreateSdDirectory(fixed_path));
}
Result OpenSdDirectory(FsDir *out, const char *path, u32 mode) {
R_TRY(EnsureSdInitialized());
R_RETURN(fsFsOpenDirectory(std::addressof(g_sd_filesystem), path, mode, out));
}
Result OpenAtmosphereSdDirectory(FsDir *out, const char *path, u32 mode) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
R_RETURN(OpenSdDirectory(out, fixed_path, mode));
}
Result OpenAtmosphereSdDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path);
R_RETURN(OpenSdDirectory(out, fixed_path, mode));
}
Result OpenAtmosphereSdRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
R_RETURN(OpenSdDirectory(out, fixed_path, mode));
}
Result OpenAtmosphereRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
R_RETURN(fsFsOpenDirectory(fs, fixed_path, mode, out));
}
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, const char *src_path) {
if (src_path[0] == '/') {
util::SNPrintf(dst_path, dst_path_size, "/atmosphere%s", src_path);
} else {
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/%s", src_path);
}
}
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, const char *subdir, const char *src_path) {
if (src_path[0] == '/') {
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/%s%s", subdir, src_path);
} else {
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/%s/%s", subdir, src_path);
}
}
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *src_path) {
if (src_path[0] == '/') {
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx%s", static_cast<u64>(program_id), src_path);
} else {
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx/%s", static_cast<u64>(program_id), src_path);
}
}
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *subdir, const char *src_path) {
if (src_path[0] == '/') {
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx/%s%s", static_cast<u64>(program_id), subdir, src_path);
} else {
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx/%s/%s", static_cast<u64>(program_id), subdir, src_path);
}
}
bool HasSdRomfsContent(ncm::ProgramId program_id) {
/* Check if romfs.bin is present. */
{
FsFile romfs_file;
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(romfs_file), program_id, "romfs.bin", OpenMode_Read))) {
fsFileClose(std::addressof(romfs_file));
return true;
}
}
/* Check for romfs folder with content. */
FsDir romfs_dir;
if (R_FAILED(OpenAtmosphereSdRomfsDirectory(std::addressof(romfs_dir), program_id, "", fs::OpenDirectoryMode_All))) {
return false;
}
ON_SCOPE_EXIT { fsDirClose(std::addressof(romfs_dir)); };
/* Verify the folder has at least one entry. */
s64 num_entries = 0;
return R_SUCCEEDED(fsDirGetEntryCount(std::addressof(romfs_dir), std::addressof(num_entries))) && num_entries > 0;
}
Result SaveAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, void *data, size_t size) {
R_TRY(EnsureSdInitialized());
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path);
/* Unconditionally create. */
/* Don't check error, as a failure here should be okay. */
FsFile f;
fsFsCreateFile(std::addressof(g_sd_filesystem), fixed_path, size, 0);
/* Try to open. */
R_TRY(fsFsOpenFile(std::addressof(g_sd_filesystem), fixed_path, OpenMode_ReadWrite, std::addressof(f)));
auto file_guard = SCOPE_GUARD { fsFileClose(std::addressof(f)); };
/* Try to set the size. */
R_TRY(fsFileSetSize(std::addressof(f), static_cast<s64>(size)));
/* Try to write data. */
R_TRY(fsFileWrite(std::addressof(f), 0, data, size, FsWriteOption_Flush));
/* Set output. */
file_guard.Cancel();
*out = f;
R_SUCCEED();
}
Result CreateAndOpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, size_t size) {
R_TRY(EnsureSdInitialized());
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path);
/* Unconditionally create. */
/* Don't check error, as a failure here should be okay. */
FsFile f;
fsFsCreateFile(std::addressof(g_sd_filesystem), fixed_path, size, 0);
/* Try to open. */
R_TRY(fsFsOpenFile(std::addressof(g_sd_filesystem), fixed_path, OpenMode_ReadWrite, std::addressof(f)));
auto file_guard = SCOPE_GUARD { fsFileClose(std::addressof(f)); };
/* Try to set the size. */
R_TRY(fsFileSetSize(std::addressof(f), static_cast<s64>(size)));
/* Set output. */
file_guard.Cancel();
*out = f;
R_SUCCEED();
}
}

View File

@@ -1,58 +0,0 @@
/*
* 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::mitm::fs {
/* Initialization. */
void OpenGlobalSdCardFileSystem();
/* Utilities. */
Result DeleteAtmosphereSdFile(const char *path);
Result CreateSdFile(const char *path, s64 size, s32 option);
Result CreateAtmosphereSdFile(const char *path, s64 size, s32 option);
Result OpenSdFile(FsFile *out, const char *path, u32 mode);
Result OpenAtmosphereSdFile(FsFile *out, const char *path, u32 mode);
Result OpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode);
Result OpenAtmosphereSdRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode);
Result OpenAtmosphereRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs);
bool HasSdFile(const char *path);
bool HasAtmosphereSdFile(const char *path);
Result CreateSdDirectory(const char *path);
Result CreateAtmosphereSdDirectory(const char *path);
Result OpenSdDirectory(FsDir *out, const char *path, u32 mode);
Result OpenAtmosphereSdDirectory(FsDir *out, const char *path, u32 mode);
Result OpenAtmosphereSdDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode);
Result OpenAtmosphereSdRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode);
Result OpenAtmosphereRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs);
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, const char *src_path);
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, const char *subdir, const char *src_path);
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *src_path);
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *subdir, const char *src_path);
bool HasSdRomfsContent(ncm::ProgramId program_id);
Result SaveAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, void *data, size_t size);
Result CreateAndOpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, size_t size);
/* NOTE: Implemented in fs.mitm logic. */
bool HasSdManualHtmlContent(ncm::ProgramId program_id);
}

View File

@@ -1,245 +0,0 @@
/*
* 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 "amsmitm_initialization.hpp"
#include "amsmitm_fs_utils.hpp"
#include "amsmitm_prodinfo_utils.hpp"
#include "bpc_mitm/bpc_ams_power_utils.hpp"
#include "set_mitm/settings_sd_kvs.hpp"
namespace ams::mitm {
namespace {
/* BIS key sources. */
constexpr u8 BisKeySources[4][2][0x10] = {
{
{0xF8, 0x3F, 0x38, 0x6E, 0x2C, 0xD2, 0xCA, 0x32, 0xA8, 0x9A, 0xB9, 0xAA, 0x29, 0xBF, 0xC7, 0x48},
{0x7D, 0x92, 0xB0, 0x3A, 0xA8, 0xBF, 0xDE, 0xE1, 0xA7, 0x4C, 0x3B, 0x6E, 0x35, 0xCB, 0x71, 0x06}
},
{
{0x41, 0x00, 0x30, 0x49, 0xDD, 0xCC, 0xC0, 0x65, 0x64, 0x7A, 0x7E, 0xB4, 0x1E, 0xED, 0x9C, 0x5F},
{0x44, 0x42, 0x4E, 0xDA, 0xB4, 0x9D, 0xFC, 0xD9, 0x87, 0x77, 0x24, 0x9A, 0xDC, 0x9F, 0x7C, 0xA4}
},
{
{0x52, 0xC2, 0xE9, 0xEB, 0x09, 0xE3, 0xEE, 0x29, 0x32, 0xA1, 0x0C, 0x1F, 0xB6, 0xA0, 0x92, 0x6C},
{0x4D, 0x12, 0xE1, 0x4B, 0x2A, 0x47, 0x4C, 0x1C, 0x09, 0xCB, 0x03, 0x59, 0xF0, 0x15, 0xF4, 0xE4}
},
{
{0x52, 0xC2, 0xE9, 0xEB, 0x09, 0xE3, 0xEE, 0x29, 0x32, 0xA1, 0x0C, 0x1F, 0xB6, 0xA0, 0x92, 0x6C},
{0x4D, 0x12, 0xE1, 0x4B, 0x2A, 0x47, 0x4C, 0x1C, 0x09, 0xCB, 0x03, 0x59, 0xF0, 0x15, 0xF4, 0xE4}
}
};
constexpr u8 BisKekSource[0x10] = {
0x34, 0xC1, 0xA0, 0xC4, 0x82, 0x58, 0xF8, 0xB4, 0xFA, 0x9E, 0x5E, 0x6A, 0xDA, 0xFC, 0x7E, 0x4F,
};
void InitializeThreadFunc(void *arg);
constexpr size_t InitializeThreadStackSize = 0x4000;
/* Globals. */
os::Event g_init_event(os::EventClearMode_ManualClear);
os::ThreadType g_initialize_thread;
alignas(os::ThreadStackAlignment) u8 g_initialize_thread_stack[InitializeThreadStackSize];
/* Console-unique data backup and protection. */
FsFile g_bis_key_file;
/* Emummc file protection. */
FsFile g_emummc_file;
/* Maintain exclusive access to the fusee external package. */
FsFile g_stratosphere_file;
FsFile g_package3_file;
constexpr inline bool IsHexadecimal(const char *str) {
while (*str) {
if (std::isxdigit(static_cast<unsigned char>(*str))) {
str++;
} else {
return false;
}
}
return true;
}
void GetBackupFileName(char *dst, size_t dst_size, const char *serial_number, const char *fn) {
if (strlen(serial_number) > 0) {
util::SNPrintf(dst, dst_size, "automatic_backups/%s_%s", serial_number, fn);
} else {
util::SNPrintf(dst, dst_size, "automatic_backups/%s", fn);
}
}
void CreateAutomaticBackups() {
/* Create a backup directory, if one doesn't exist. */
mitm::fs::CreateAtmosphereSdDirectory("/automatic_backups");
/* Initialize PRODINFO and get a reference for the device. */
char device_reference[0x40] = {};
ON_SCOPE_EXIT { std::memset(device_reference, 0, sizeof(device_reference)); };
mitm::SaveProdInfoBackupsAndWipeMemory(device_reference, sizeof(device_reference));
/* Backup BIS keys. */
{
u64 key_generation = 0;
if (hos::GetVersion() >= hos::Version_5_0_0) {
R_ABORT_UNLESS(spl::GetConfig(std::addressof(key_generation), spl::ConfigItem::DeviceUniqueKeyGeneration));
}
u8 bis_keys[4][2][0x10];
std::memset(bis_keys, 0xCC, sizeof(bis_keys));
ON_SCOPE_EXIT { std::memset(bis_keys, 0xCC, sizeof(bis_keys)); };
/* TODO: Clean this up. */
for (size_t partition = 0; partition < 4; partition++) {
if (partition == 0) {
for (size_t i = 0; i < 2; i++) {
R_ABORT_UNLESS(spl::GenerateSpecificAesKey(bis_keys[partition][i], 0x10, BisKeySources[partition][i], 0x10, key_generation, i));
}
} else {
const u32 option = (partition == 3 && spl::IsRecoveryBoot()) ? 0x4 : 0x1;
spl::AccessKey access_key;
R_ABORT_UNLESS(spl::GenerateAesKek(std::addressof(access_key), BisKekSource, 0x10, key_generation, option));
for (size_t i = 0; i < 2; i++) {
R_ABORT_UNLESS(spl::GenerateAesKey(bis_keys[partition][i], 0x10, access_key, BisKeySources[partition][i], 0x10));
}
}
}
char bis_keys_backup_name[ams::fs::EntryNameLengthMax + 1];
GetBackupFileName(bis_keys_backup_name, sizeof(bis_keys_backup_name), device_reference, "BISKEYS.bin");
mitm::fs::CreateAtmosphereSdFile(bis_keys_backup_name, sizeof(bis_keys), ams::fs::CreateOption_None);
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(g_bis_key_file), bis_keys_backup_name, ams::fs::OpenMode_ReadWrite));
R_ABORT_UNLESS(fsFileSetSize(std::addressof(g_bis_key_file), sizeof(bis_keys)));
R_ABORT_UNLESS(fsFileWrite(std::addressof(g_bis_key_file), 0, bis_keys, sizeof(bis_keys), FsWriteOption_Flush));
/* NOTE: g_bis_key_file is intentionally not closed here. This prevents any other process from opening it. */
}
/* Open a reference to the fusee external package. */
/* As upcoming/current atmosphere releases may contain more than one zip which users much choose between, */
/* maintaining an open reference prevents cleanly the issue of "automatic" updaters selecting the incorrect */
/* zip, and encourages good updating hygiene -- atmosphere should not be updated on SD while HOS is alive. */
{
R_ABORT_UNLESS(mitm::fs::OpenSdFile(std::addressof(g_package3_file), "/atmosphere/package3", ams::fs::OpenMode_Read));
R_ABORT_UNLESS(mitm::fs::OpenSdFile(std::addressof(g_stratosphere_file), "/atmosphere/stratosphere.romfs", ams::fs::OpenMode_Read));
}
}
/* Initialization implementation */
void InitializeThreadFunc(void *) {
/* Wait for the SD card to be ready. */
cfg::WaitSdCardInitialized();
/* Open global SD card file system, so that other threads can begin using the SD. */
mitm::fs::OpenGlobalSdCardFileSystem();
/* Mount the sd card at a convenient mountpoint. */
ams::fs::MountSdCard(ams::fs::impl::SdCardFileSystemMountName);
/* Initialize the reboot manager (load a payload off the SD). */
/* Discard result, since it doesn't need to succeed. */
mitm::bpc::LoadRebootPayload();
/* Backup Calibration Binary and BIS keys. */
CreateAutomaticBackups();
/* If we're emummc, persist a write-handle to prevent other processes from touching the image. */
if (emummc::IsActive()) {
if (const char *emummc_file_path = emummc::GetFilePath(); emummc_file_path != nullptr) {
char emummc_path[ams::fs::EntryNameLengthMax + 1];
util::SNPrintf(emummc_path, sizeof(emummc_path), "%s/eMMC", emummc_file_path);
mitm::fs::OpenSdFile(std::addressof(g_emummc_file), emummc_path, ams::fs::OpenMode_Read);
}
/* NOTE: due to an Atmosphere bug, NS accesses to the Nintendo dir accessed /Nintendo/Nintendo */
/* instead of /Nintendo. This logic is potentially temporary, and fixes the case where this would have happened. */
{
auto HasDir = [](const char *p) -> bool { bool res{}; R_ABORT_UNLESS(ams::fs::HasDirectory(std::addressof(res), p)); return res; };
auto HasFile = [](const char *p) -> bool { bool res{}; R_ABORT_UNLESS(ams::fs::HasFile(std::addressof(res), p)); return res; };
char emummc_path[ams::fs::EntryNameLengthMax + 1];
char emummc_bug_path[ams::fs::EntryNameLengthMax + 1];
util::SNPrintf(emummc_path, sizeof(emummc_path), "%s:/%s", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
util::SNPrintf(emummc_bug_path, sizeof(emummc_bug_path), "%s:/%s/Nintendo", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
if (HasDir(emummc_bug_path)) {
/* Ensure Contents directory exists for normal emummc. */
/* NOTE: Allowed to fail on already-exists. */
util::SNPrintf(emummc_path, sizeof(emummc_path), "%s:/%s/Contents", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
ams::fs::CreateDirectory(emummc_path);
/* Fix Contents/private */
util::SNPrintf(emummc_path, sizeof(emummc_path), "%s:/%s/Contents/private", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
util::SNPrintf(emummc_bug_path, sizeof(emummc_bug_path), "%s:/%s/Nintendo/Contents/private", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
if (HasFile(emummc_bug_path) && !HasFile(emummc_path)) {
R_ABORT_UNLESS(ams::fs::RenameFile(emummc_bug_path, emummc_path));
}
/* Fix Contents/private1 */
util::SNPrintf(emummc_path, sizeof(emummc_path), "%s:/%s/Contents/private1", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
util::SNPrintf(emummc_bug_path, sizeof(emummc_bug_path), "%s:/%s/Nintendo/Contents/private1", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
if (HasFile(emummc_bug_path) && !HasFile(emummc_path)) {
R_ABORT_UNLESS(ams::fs::RenameFile(emummc_bug_path, emummc_path));
}
/* Delete bug directory. */
util::SNPrintf(emummc_bug_path, sizeof(emummc_bug_path), "%s:/%s/Nintendo", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
R_ABORT_UNLESS(ams::fs::DeleteDirectoryRecursively(emummc_bug_path));
}
}
}
/* Connect to set:sys. */
R_ABORT_UNLESS(setInitialize());
R_ABORT_UNLESS(setsysInitialize());
/* Load settings off the SD card. */
settings::fwdbg::InitializeSdCardKeyValueStore();
/* Ensure that we reboot using the user's preferred method. */
R_ABORT_UNLESS(mitm::bpc::DetectPreferredRebootFunctionality());
/* Signal to waiters that we are ready. */
g_init_event.Signal();
}
}
void StartInitialize() {
/* Initialize prodinfo. */
mitm::InitializeProdInfoManagement();
/* Launch initialize thread. */
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_initialize_thread), InitializeThreadFunc, nullptr, g_initialize_thread_stack, sizeof(g_initialize_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm, InitializeThread)));
os::SetThreadNamePointer(std::addressof(g_initialize_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm, InitializeThread));
os::StartThread(std::addressof(g_initialize_thread));
}
bool IsInitialized() {
return g_init_event.TryWait();
}
void WaitInitialized() {
g_init_event.Wait();
}
}

View File

@@ -1,26 +0,0 @@
/*
* 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::mitm {
void StartInitialize();
bool IsInitialized();
void WaitInitialized();
}

View File

@@ -1,93 +0,0 @@
/*
* 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 "amsmitm_initialization.hpp"
#include "amsmitm_module_management.hpp"
#include "bpc_mitm/bpc_ams_power_utils.hpp"
#include "sysupdater/sysupdater_fs_utils.hpp"
namespace ams {
namespace {
/* TODO: we really shouldn't be using malloc just to avoid dealing with real allocator separation. */
constexpr size_t MallocBufferSize = 12_MB;
alignas(os::MemoryPageSize) constinit u8 g_malloc_buffer[MallocBufferSize];
}
namespace init {
void InitializeSystemModule() {
/* Initialize our connection to sm. */
R_ABORT_UNLESS(sm::Initialize());
/* Initialize fs. */
fs::InitializeForSystem();
fs::SetEnabledAutoAbort(false);
/* Initialize other services. */
R_ABORT_UNLESS(pmdmntInitialize());
R_ABORT_UNLESS(pminfoInitialize());
ncm::Initialize();
/* Verify that we can sanely execute. */
ams::CheckApiVersion();
}
void FinalizeSystemModule() { /* ... */ }
void Startup() {
/* Initialize the global malloc allocator. */
init::InitializeAllocator(g_malloc_buffer, sizeof(g_malloc_buffer));
}
}
void ExceptionHandler(FatalErrorContext *ctx) {
/* We're bpc-mitm (or ams_mitm, anyway), so manually reboot to fatal error. */
mitm::bpc::RebootForFatalError(ctx);
}
void NORETURN Exit(int rc) {
AMS_UNUSED(rc);
AMS_ABORT("Exit called by immortal process");
}
void Main() {
/* Register "ams" port, use up its session. */
{
svc::Handle ams_port;
R_ABORT_UNLESS(svc::ManageNamedPort(std::addressof(ams_port), "ams", 1));
svc::Handle ams_session;
R_ABORT_UNLESS(svc::ConnectToNamedPort(std::addressof(ams_session), "ams"));
}
/* Initialize fssystem library. */
fssystem::InitializeForAtmosphereMitm();
/* Configure ncm to use fssystem library to mount content from the sd card. */
ncm::SetMountContentMetaFunction(mitm::sysupdater::MountSdCardContentMeta);
/* Launch all mitm modules in sequence. */
mitm::LaunchAllModules();
/* Wait for all mitm modules to end. */
mitm::WaitAllModules();
}
}

View File

@@ -1,48 +0,0 @@
/*
* 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::mitm {
template<typename T>
concept IsModule = requires(T, void *arg) {
{ T::ThreadPriority } -> std::convertible_to<s32>;
{ T::StackSize } -> std::convertible_to<size_t>;
{ T::Stack } -> std::convertible_to<void *>;
{ T::ThreadFunction(arg) } -> std::same_as<void>;
};
#define DEFINE_MITM_MODULE_CLASS(ss, prio) class MitmModule { \
public: \
static constexpr s32 ThreadPriority = prio; \
static constexpr size_t StackSize = ss; \
alignas(os::ThreadStackAlignment) static inline u8 Stack[StackSize]; \
public: \
static void ThreadFunction(void *); \
}
template<class M> requires IsModule<M>
struct ModuleTraits {
static constexpr void *Stack = &M::Stack[0];
static constexpr size_t StackSize = M::StackSize;
static constexpr s32 ThreadPriority = M::ThreadPriority;
static constexpr ::ThreadFunc ThreadFunction = &M::ThreadFunction;
};
}

View File

@@ -1,100 +0,0 @@
/*
* 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 "amsmitm_module_management.hpp"
#include "amsmitm_module.hpp"
#include "fs_mitm/fsmitm_module.hpp"
#include "set_mitm/setmitm_module.hpp"
#include "bpc_mitm/bpcmitm_module.hpp"
#include "bpc_mitm/bpc_ams_module.hpp"
#include "ns_mitm/nsmitm_module.hpp"
#include "dns_mitm/dnsmitm_module.hpp"
#include "sysupdater/sysupdater_module.hpp"
#include "mitm_pm/mitm_pm_module.hpp"
namespace ams::mitm {
namespace {
enum ModuleId : u32 {
ModuleId_FsMitm,
ModuleId_SetMitm,
ModuleId_BpcMitm,
ModuleId_BpcAms,
ModuleId_NsMitm,
ModuleId_DnsMitm,
ModuleId_Sysupdater,
ModuleId_PmService,
ModuleId_Count,
};
struct ModuleDefinition {
ThreadFunc main;
void *stack_mem;
s32 priority;
u32 stack_size;
};
template<class M>
constexpr ModuleDefinition GetModuleDefinition() {
using Traits = ModuleTraits<M>;
return ModuleDefinition {
.main = Traits::ThreadFunction,
.stack_mem = Traits::Stack,
.priority = Traits::ThreadPriority,
.stack_size = static_cast<u32>(Traits::StackSize),
};
}
ams::os::ThreadType g_module_threads[ModuleId_Count];
constexpr ModuleDefinition g_module_definitions[ModuleId_Count] = {
GetModuleDefinition<fs::MitmModule>(),
GetModuleDefinition<settings::MitmModule>(),
GetModuleDefinition<bpc::MitmModule>(),
GetModuleDefinition<bpc_ams::MitmModule>(),
GetModuleDefinition<ns::MitmModule>(),
GetModuleDefinition<socket::resolver::MitmModule>(),
GetModuleDefinition<sysupdater::MitmModule>(),
GetModuleDefinition<pm::MitmModule>(),
};
}
void LaunchAllModules() {
/* Create thread for each module. */
for (u32 i = 0; i < static_cast<u32>(ModuleId_Count); i++) {
const ModuleDefinition &cur_module = g_module_definitions[i];
R_ABORT_UNLESS(os::CreateThread(g_module_threads + i, cur_module.main, nullptr, cur_module.stack_mem, cur_module.stack_size, cur_module.priority));
}
/* Start thread for each module. */
for (u32 i = 0; i < static_cast<u32>(ModuleId_Count); i++) {
os::StartThread(g_module_threads + i);
}
}
void WaitAllModules() {
/* Wait on thread for each module. */
for (u32 i = 0; i < static_cast<u32>(ModuleId_Count); i++) {
os::WaitThread(g_module_threads + i);
}
}
}

View File

@@ -1,24 +0,0 @@
/*
* 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::mitm {
void LaunchAllModules();
void WaitAllModules();
}

View File

@@ -1,637 +0,0 @@
/*
* 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 "amsmitm_fs_utils.hpp"
#include "amsmitm_prodinfo_utils.hpp"
namespace ams::mitm {
namespace {
constexpr inline u16 Crc16InitialValue = 0x55AA;
constexpr inline u16 Crc16Table[] = {
0x0000, 0xCC01, 0xD801, 0x1400,
0xF001, 0x3C00, 0x2800, 0xE401,
0xA001, 0x6C00, 0x7800, 0xB401,
0x5000, 0x9C01, 0x8801, 0x4400,
};
u16 GetCrc16(const void *data, size_t size) {
AMS_ASSERT(data != nullptr);
AMS_ASSERT(size > 0);
const u8 *src = static_cast<const u8 *>(data);
u16 crc = Crc16InitialValue;
u16 tmp = 0;
while ((size--) > 0) {
tmp = Crc16Table[crc & 0xF];
crc = ((crc >> 4) & 0x0FFF) ^ tmp ^ Crc16Table[*src & 0xF];
tmp = Crc16Table[crc & 0xF];
crc = ((crc >> 4) & 0x0FFF) ^ tmp ^ Crc16Table[(*(src++) >> 4) & 0xF];
}
return crc;
}
bool IsBlank(const void *data, size_t size) {
AMS_ASSERT(data != nullptr);
AMS_ASSERT(size > 0);
const u8 *src = static_cast<const u8 *>(data);
while ((size--) > 0) {
if (*(src++) != 0) {
return false;
}
}
return true;
}
constexpr inline u32 CalibrationMagic = util::FourCC<'C','A','L','0'>::Code;
struct Sha256Hash {
u8 data[crypto::Sha256Generator::HashSize];
};
struct CalibrationInfoHeader {
u32 magic;
u32 version;
u32 body_size;
u16 model;
u16 update_count;
u8 pad[0xE];
u16 crc;
Sha256Hash body_hash;
};
static_assert(sizeof(CalibrationInfoHeader) == 0x40);
constexpr inline size_t CalibrationInfoBodySizeMax = CalibrationBinarySize - sizeof(CalibrationInfoHeader);
struct CalibrationInfo {
CalibrationInfoHeader header;
u8 body[CalibrationInfoBodySizeMax]; /* TODO: CalibrationInfoBody body; */
template<typename Block>
Block &GetBlock() {
static_assert(Block::Offset >= sizeof(CalibrationInfoHeader));
static_assert(Block::Offset < sizeof(CalibrationInfo));
static_assert(Block::Offset + Block::Size <= sizeof(CalibrationInfo));
return *static_cast<Block *>(static_cast<void *>(std::addressof(this->body[Block::Offset - sizeof(this->header)])));
}
template<typename Block>
const Block &GetBlock() const {
static_assert(Block::Offset >= sizeof(CalibrationInfoHeader));
static_assert(Block::Offset < sizeof(CalibrationInfo));
static_assert(Block::Offset + Block::Size <= sizeof(CalibrationInfo));
return *static_cast<const Block *>(static_cast<const void *>(std::addressof(this->body[Block::Offset - sizeof(this->header)])));
}
};
static_assert(sizeof(CalibrationInfo) == CalibrationBinarySize);
struct SecureCalibrationInfoBackup {
CalibrationInfo info;
Sha256Hash hash;
u8 pad[SecureCalibrationBinaryBackupSize - sizeof(info) - sizeof(hash)];
};
static_assert(sizeof(SecureCalibrationInfoBackup) == SecureCalibrationBinaryBackupSize);
bool IsValidSha256Hash(const Sha256Hash &hash, const void *data, size_t data_size) {
Sha256Hash calc_hash;
ON_SCOPE_EXIT { ::ams::crypto::ClearMemory(std::addressof(calc_hash), sizeof(calc_hash)); };
::ams::crypto::GenerateSha256(std::addressof(calc_hash), sizeof(calc_hash), data, data_size);
return ::ams::crypto::IsSameBytes(std::addressof(calc_hash), std::addressof(hash), sizeof(Sha256Hash));
}
bool IsValid(const CalibrationInfoHeader &header) {
return header.magic == CalibrationMagic && GetCrc16(std::addressof(header), AMS_OFFSETOF(CalibrationInfoHeader, crc)) == header.crc;
}
bool IsValid(const CalibrationInfoHeader &header, const void *body) {
return IsValid(header) && IsValidSha256Hash(header.body_hash, body, header.body_size);
}
#define DEFINE_CALIBRATION_CRC_BLOCK(_TypeName, _Offset, _Size, _Decl, _MemberName) \
struct _TypeName { \
static constexpr size_t Offset = _Offset; \
static constexpr size_t Size = _Size; \
static constexpr bool IsCrcBlock = true; \
static constexpr bool IsShaBlock = false; \
_Decl; \
static_assert(Size >= sizeof(_MemberName) + sizeof(u16)); \
u8 pad[Size - sizeof(_MemberName) - sizeof(u16)]; \
u16 crc; \
}; \
static_assert(sizeof(_TypeName) == _TypeName::Size)
#define DEFINE_CALIBRATION_SHA_BLOCK(_TypeName, _Offset, _Size, _Decl, _MemberName) \
struct _TypeName { \
static constexpr size_t Offset = _Offset; \
static constexpr size_t Size = _Size; \
static constexpr bool IsCrcBlock = false; \
static constexpr bool IsShaBlock = true; \
_Decl; \
static_assert(Size == sizeof(_MemberName) + sizeof(Sha256Hash)); \
Sha256Hash sha256_hash; \
}; \
static_assert(sizeof(_TypeName) == _TypeName::Size)
DEFINE_CALIBRATION_CRC_BLOCK(SerialNumberBlock, 0x0250, 0x020, ::ams::settings::factory::SerialNumber serial_number, serial_number);
DEFINE_CALIBRATION_CRC_BLOCK(EccB233DeviceCertificateBlock, 0x0480, 0x190, ::ams::settings::factory::EccB233DeviceCertificate device_certificate, device_certificate);
DEFINE_CALIBRATION_CRC_BLOCK(SslKeyBlock, 0x09B0, 0x120, u8 ssl_key[0x110], ssl_key);
DEFINE_CALIBRATION_CRC_BLOCK(SslCertificateSizeBlock, 0x0AD0, 0x010, u64 ssl_certificate_size, ssl_certificate_size);
DEFINE_CALIBRATION_SHA_BLOCK(SslCertificateBlock, 0x0AE0, 0x820, u8 ssl_certificate[0x800], ssl_certificate);
DEFINE_CALIBRATION_CRC_BLOCK(EcqvEcdsaAmiiboRootCertificateBlock, 0x35A0, 0x080, u8 data[0x70], data);
DEFINE_CALIBRATION_CRC_BLOCK(EcqvBlsAmiiboRootCertificateBlock, 0x36A0, 0x0A0, u8 data[0x90], data);
DEFINE_CALIBRATION_CRC_BLOCK(ExtendedSslKeyBlock, 0x3AE0, 0x140, u8 ssl_key[0x134], ssl_key);
DEFINE_CALIBRATION_CRC_BLOCK(Rsa2048DeviceKeyBlock, 0x3D70, 0x250, u8 device_key[0x240], device_key);
DEFINE_CALIBRATION_CRC_BLOCK(Rsa2048DeviceCertificateBlock, 0x3FC0, 0x250, ::ams::settings::factory::Rsa2048DeviceCertificate device_certificate, device_certificate);
#undef DEFINE_CALIBRATION_CRC_BLOCK
#undef DEFINE_CALIBRATION_SHA_BLOCK
constexpr inline const char BlankSerialNumberString[] = "XAW00000000000";
template<typename Block>
void Blank(Block &block) {
if constexpr (std::is_same<Block, SerialNumberBlock>::value) {
static_assert(sizeof(BlankSerialNumberString) <= sizeof(SerialNumberBlock::serial_number));
std::memset(std::addressof(block), 0, Block::Size - sizeof(block.crc));
std::memcpy(block.serial_number.str, BlankSerialNumberString, sizeof(BlankSerialNumberString));
block.crc = GetCrc16(std::addressof(block), Block::Size - sizeof(block.crc));
} else if constexpr (std::is_same<Block, SslCertificateBlock>::value) {
std::memset(std::addressof(block), 0, sizeof(block.ssl_certificate));
} else if constexpr (Block::IsCrcBlock) {
std::memset(std::addressof(block), 0, Block::Size - sizeof(block.crc));
block.crc = GetCrc16(std::addressof(block), Block::Size - sizeof(block.crc));
} else {
static_assert(Block::IsShaBlock);
std::memset(std::addressof(block), 0, Block::Size);
::ams::crypto::GenerateSha256(std::addressof(block.sha256_hash), sizeof(block.sha256_hash), std::addressof(block), Block::Size - sizeof(block.sha256_hash));
}
}
template<typename Block>
bool IsBlank(const Block &block) {
if constexpr (std::is_same<Block, SerialNumberBlock>::value) {
static_assert(sizeof(BlankSerialNumberString) <= sizeof(SerialNumberBlock::serial_number));
return std::memcmp(block.serial_number.str, BlankSerialNumberString, sizeof(BlankSerialNumberString) - 1) == 0 || IsBlank(std::addressof(block), Block::Size - sizeof(block.crc));
} else if constexpr (Block::IsCrcBlock) {
return IsBlank(std::addressof(block), Block::Size - sizeof(block.crc));
} else {
return IsBlank(std::addressof(block), Block::Size - sizeof(block.sha256_hash));
}
}
template<typename Block>
bool IsValid(const Block &block, size_t size = 0) {
if constexpr (Block::IsCrcBlock) {
return GetCrc16(std::addressof(block), Block::Size - sizeof(block.crc)) == block.crc;
} else {
static_assert(Block::IsShaBlock);
return IsValidSha256Hash(block.sha256_hash, std::addressof(block), size != 0 ? size : Block::Size - sizeof(block.sha256_hash));
}
}
void Blank(CalibrationInfo &info) {
/* Set header. */
info.header.magic = CalibrationMagic;
info.header.body_size = sizeof(info.body);
info.header.crc = GetCrc16(std::addressof(info.header), AMS_OFFSETOF(CalibrationInfoHeader, crc));
/* Set blocks. */
Blank(info.GetBlock<SerialNumberBlock>());
Blank(info.GetBlock<SslCertificateSizeBlock>());
Blank(info.GetBlock<SslCertificateBlock>());
Blank(info.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>());
Blank(info.GetBlock<EcqvBlsAmiiboRootCertificateBlock>());
Blank(info.GetBlock<ExtendedSslKeyBlock>());
/* Set header hash. */
crypto::GenerateSha256(std::addressof(info.header.body_hash), sizeof(info.header.body_hash), std::addressof(info.body), sizeof(info.body));
}
bool IsValidHeader(const CalibrationInfo &cal) {
return IsValid(cal.header) && cal.header.body_size <= CalibrationInfoBodySizeMax && IsValid(cal.header, cal.body);
}
bool IsValidSerialNumber(const char *sn) {
for (size_t i = 0; i < std::strlen(sn); i++) {
if (!std::isalnum(static_cast<unsigned char>(sn[i]))) {
return false;
}
}
return true;
}
void GetSerialNumber(char *dst, const CalibrationInfo &info) {
std::memcpy(dst, std::addressof(info.GetBlock<SerialNumberBlock>()), sizeof(info.GetBlock<SerialNumberBlock>().serial_number));
dst[sizeof(info.GetBlock<SerialNumberBlock>().serial_number) + 1] = '\x00';
}
bool IsValidSerialNumber(const CalibrationInfo &cal) {
char sn[0x20] = {};
ON_SCOPE_EXIT { std::memset(sn, 0, sizeof(sn)); };
GetSerialNumber(sn, cal);
return IsValidSerialNumber(sn);
}
bool IsValid(const CalibrationInfo &cal) {
return IsValidHeader(cal) &&
IsValid(cal.GetBlock<SerialNumberBlock>()) &&
IsValid(cal.GetBlock<EccB233DeviceCertificateBlock>()) &&
IsValid(cal.GetBlock<SslKeyBlock>()) &&
IsValid(cal.GetBlock<SslCertificateSizeBlock>()) &&
cal.GetBlock<SslCertificateSizeBlock>().ssl_certificate_size <= sizeof(cal.GetBlock<SslCertificateBlock>().ssl_certificate) &&
IsValid(cal.GetBlock<SslCertificateBlock>(), cal.GetBlock<SslCertificateSizeBlock>().ssl_certificate_size) &&
IsValid(cal.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>()) &&
IsValid(cal.GetBlock<EcqvBlsAmiiboRootCertificateBlock>()) &&
IsValid(cal.GetBlock<ExtendedSslKeyBlock>()) &&
IsValidSerialNumber(cal);
}
bool ContainsCorrectDeviceId(const EccB233DeviceCertificateBlock &block, u64 device_id) {
static constexpr size_t DeviceIdOffset = 0xC6;
char found_device_id_str[sizeof("0011223344556677")] = {};
ON_SCOPE_EXIT { std::memset(found_device_id_str, 0, sizeof(found_device_id_str)); };
std::memcpy(found_device_id_str, std::addressof(block.device_certificate.data[DeviceIdOffset]), sizeof(found_device_id_str) - 1);
static constexpr u64 DeviceIdLowMask = 0x00FFFFFFFFFFFFFFul;
return (std::strtoul(found_device_id_str, nullptr, 16) & DeviceIdLowMask) == (device_id & DeviceIdLowMask);
}
bool ContainsCorrectDeviceId(const CalibrationInfo &cal) {
return ContainsCorrectDeviceId(cal.GetBlock<EccB233DeviceCertificateBlock>(), exosphere::GetDeviceId());
}
bool IsValidForSecureBackup(const CalibrationInfo &cal) {
return IsValid(cal) && ContainsCorrectDeviceId(cal);
}
bool IsBlank(const CalibrationInfo &cal) {
return IsBlank(cal.GetBlock<SerialNumberBlock>()) ||
IsBlank(cal.GetBlock<SslCertificateSizeBlock>()) ||
IsBlank(cal.GetBlock<SslCertificateBlock>()) ||
IsBlank(cal.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>()) ||
IsBlank(cal.GetBlock<EcqvBlsAmiiboRootCertificateBlock>()) ||
IsBlank(cal.GetBlock<ExtendedSslKeyBlock>());
}
void ReadStorageCalibrationBinary(CalibrationInfo *out) {
FsStorage calibration_binary_storage;
R_ABORT_UNLESS(fsOpenBisStorage(std::addressof(calibration_binary_storage), FsBisPartitionId_CalibrationBinary));
ON_SCOPE_EXIT { fsStorageClose(std::addressof(calibration_binary_storage)); };
R_ABORT_UNLESS(fsStorageRead(std::addressof(calibration_binary_storage), 0, out, sizeof(*out)));
}
constexpr inline const u8 SecureCalibrationBinaryBackupIv[crypto::Aes128CtrDecryptor::IvSize] = {};
void ReadStorageEncryptedSecureCalibrationBinaryBackupUnsafe(SecureCalibrationInfoBackup *out) {
FsStorage calibration_binary_storage;
R_ABORT_UNLESS(fsOpenBisStorage(std::addressof(calibration_binary_storage), FsBisPartitionId_CalibrationBinary));
ON_SCOPE_EXIT { fsStorageClose(std::addressof(calibration_binary_storage)); };
R_ABORT_UNLESS(fsStorageRead(std::addressof(calibration_binary_storage), SecureCalibrationInfoBackupOffset, out, sizeof(*out)));
}
void WriteStorageEncryptedSecureCalibrationBinaryBackupUnsafe(const SecureCalibrationInfoBackup *src) {
FsStorage calibration_binary_storage;
R_ABORT_UNLESS(fsOpenBisStorage(std::addressof(calibration_binary_storage), FsBisPartitionId_CalibrationBinary));
ON_SCOPE_EXIT { fsStorageClose(std::addressof(calibration_binary_storage)); };
R_ABORT_UNLESS(fsStorageWrite(std::addressof(calibration_binary_storage), SecureCalibrationInfoBackupOffset, src, sizeof(*src)));
}
void GenerateSecureCalibrationBinaryBackupKey(void *dst, size_t dst_size) {
static constexpr const u8 SecureCalibrationBinaryBackupKeySource[crypto::Aes128CtrDecryptor::KeySize] = { '|', '-', 'A', 'M', 'S', '-', 'C', 'A', 'L', '0', '-', 'K', 'E', 'Y', '-', '|' };
spl::AccessKey access_key;
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(access_key), sizeof(access_key)); };
/* Generate a personalized kek. */
R_ABORT_UNLESS(spl::GenerateAesKek(std::addressof(access_key), SecureCalibrationBinaryBackupKeySource, sizeof(SecureCalibrationBinaryBackupKeySource), 0, 1));
/* Generate a personalized key. */
R_ABORT_UNLESS(spl::GenerateAesKey(dst, dst_size, access_key, SecureCalibrationBinaryBackupKeySource, sizeof(SecureCalibrationBinaryBackupKeySource)));
}
bool ReadStorageSecureCalibrationBinaryBackup(SecureCalibrationInfoBackup *out) {
/* Read the data. */
ReadStorageEncryptedSecureCalibrationBinaryBackupUnsafe(out);
/* Don't leak any data unless we validate. */
auto clear_guard = SCOPE_GUARD { std::memset(out, 0, sizeof(*out)); };
{
/* Create a buffer to hold our key. */
u8 key[crypto::Aes128CtrDecryptor::KeySize];
ON_SCOPE_EXIT { crypto::ClearMemory(key, sizeof(key)); };
/* Generate the key. */
GenerateSecureCalibrationBinaryBackupKey(key, sizeof(key));
/* Decrypt the data in place. */
crypto::DecryptAes128Ctr(out, sizeof(*out), key, sizeof(key), SecureCalibrationBinaryBackupIv, sizeof(SecureCalibrationBinaryBackupIv), out, sizeof(*out));
}
/* Generate a hash for the data. */
if (!IsValidSha256Hash(out->hash, std::addressof(out->info), sizeof(out->info))) {
return false;
}
/* Validate the backup. */
if (!IsValidForSecureBackup(out->info)) {
return false;
}
/* Our backup is valid. */
clear_guard.Cancel();
return true;
}
void WriteStorageSecureCalibrationBinaryBackup(SecureCalibrationInfoBackup *src) {
/* Clear the input once we've written it. */
ON_SCOPE_EXIT { std::memset(src, 0, sizeof(*src)); };
/* Ensure that the input is valid. */
AMS_ABORT_UNLESS(IsValidForSecureBackup(src->info));
/* Set the Sha256 hash. */
crypto::GenerateSha256(std::addressof(src->hash), sizeof(src->hash), std::addressof(src->info), sizeof(src->info));
/* Validate the hash. */
AMS_ABORT_UNLESS(IsValidSha256Hash(src->hash, std::addressof(src->info), sizeof(src->info)));
/* Encrypt the data. */
{
/* Create a buffer to hold our key. */
u8 key[crypto::Aes128CtrDecryptor::KeySize];
ON_SCOPE_EXIT { crypto::ClearMemory(key, sizeof(key)); };
/* Generate the key. */
GenerateSecureCalibrationBinaryBackupKey(key, sizeof(key));
/* Encrypt the data in place. */
crypto::EncryptAes128Ctr(src, sizeof(*src), key, sizeof(key), SecureCalibrationBinaryBackupIv, sizeof(SecureCalibrationBinaryBackupIv), src, sizeof(*src));
}
/* Write the encrypted data. */
WriteStorageEncryptedSecureCalibrationBinaryBackupUnsafe(src);
}
void GetBackupFileName(char *dst, size_t dst_size, const CalibrationInfo &info) {
char sn[0x20] = {};
ON_SCOPE_EXIT { std::memset(sn, 0, sizeof(sn)); };
if (IsValidForSecureBackup(info)) {
GetSerialNumber(sn, info);
util::SNPrintf(dst, dst_size, "automatic_backups/%s_PRODINFO.bin", sn);
} else {
Sha256Hash hash;
crypto::GenerateSha256(std::addressof(hash), sizeof(hash), std::addressof(info), sizeof(info));
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(hash), sizeof(hash)); };
if (IsValid(info)) {
if (IsBlank(info)) {
util::SNPrintf(dst, dst_size, "automatic_backups/BLANK_PRODINFO_%02X%02X%02X%02X.bin", hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
} else {
GetSerialNumber(sn, info);
util::SNPrintf(dst, dst_size, "automatic_backups/%s_PRODINFO_%02X%02X%02X%02X.bin", sn, hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
}
} else {
util::SNPrintf(dst, dst_size, "automatic_backups/INVALID_PRODINFO_%02X%02X%02X%02X.bin", hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
}
}
}
void SafeRead(ams::fs::fsa::IFile *file, s64 offset, void *dst, size_t size) {
size_t read_size = 0;
R_ABORT_UNLESS(file->Read(std::addressof(read_size), offset, dst, size));
AMS_ABORT_UNLESS(read_size == size);
}
alignas(os::MemoryPageSize) CalibrationInfo g_temp_calibration_info = {};
void SaveProdInfoBackup(util::optional<ams::fs::FileStorage> *dst, const CalibrationInfo &info) {
char backup_fn[0x100];
GetBackupFileName(backup_fn, sizeof(backup_fn), info);
/* Create the file, in case it does not exist. */
mitm::fs::CreateAtmosphereSdFile(backup_fn, sizeof(CalibrationInfo), ams::fs::CreateOption_None);
/* Open the file. */
FsFile libnx_file;
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(libnx_file), backup_fn, ams::fs::OpenMode_ReadWrite));
/* Create our accessor. */
std::unique_ptr<ams::fs::fsa::IFile> file = std::make_unique<ams::fs::RemoteFile>(libnx_file);
AMS_ABORT_UNLESS(file != nullptr);
/* Check if we're valid already. */
bool valid = false;
s64 size;
R_ABORT_UNLESS(file->GetSize(std::addressof(size)));
if (size == sizeof(CalibrationInfo)) {
SafeRead(file.get(), 0, std::addressof(g_temp_calibration_info), sizeof(g_temp_calibration_info));
ON_SCOPE_EXIT { std::memset(std::addressof(g_temp_calibration_info), 0, sizeof(g_temp_calibration_info)); };
if (std::memcmp(std::addressof(info), std::addressof(g_temp_calibration_info), sizeof(CalibrationInfo)) == 0) {
valid = true;
}
}
/* If we're not valid, we need to save. */
if (!valid) {
R_ABORT_UNLESS(file->Write(0, std::addressof(info), sizeof(info), ams::fs::WriteOption::Flush));
}
/* Save our storage to output. */
if (dst != nullptr) {
dst->emplace(std::move(file));
}
}
void GetRandomEntropy(Sha256Hash *dst) {
AMS_ASSERT(dst != nullptr);
u64 data_buffer[3] = {};
ON_SCOPE_EXIT { crypto::ClearMemory(data_buffer, sizeof(data_buffer)); };
data_buffer[0] = os::GetSystemTick().GetInt64Value();
R_ABORT_UNLESS(svc::GetInfo(data_buffer + 1, svc::InfoType_AliasRegionAddress, svc::PseudoHandle::CurrentProcess, 0));
if (hos::GetVersion() >= hos::Version_2_0_0) {
R_ABORT_UNLESS(svc::GetInfo(data_buffer + 2, svc::InfoType_RandomEntropy, svc::InvalidHandle, (data_buffer[0] ^ (data_buffer[1] >> 24)) & 3));
} else {
data_buffer[2] = os::GetSystemTick().GetInt64Value();
}
return crypto::GenerateSha256(dst, sizeof(*dst), data_buffer, sizeof(data_buffer));
}
void FillWithGarbage(void *dst, size_t dst_size) {
/* Get random entropy. */
Sha256Hash entropy;
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(entropy), sizeof(entropy)); };
GetRandomEntropy(std::addressof(entropy));
/* Clear dst. */
std::memset(dst, 0xCC, dst_size);
/* Encrypt dst. */
static_assert(sizeof(entropy) == crypto::Aes128CtrEncryptor::KeySize + crypto::Aes128CtrEncryptor::IvSize);
crypto::EncryptAes128Ctr(dst, dst_size, entropy.data, crypto::Aes128CtrEncryptor::KeySize, entropy.data + crypto::Aes128CtrEncryptor::KeySize, crypto::Aes128CtrEncryptor::IvSize, dst, dst_size);
}
alignas(os::MemoryPageSize) constinit CalibrationInfo g_calibration_info = {};
alignas(os::MemoryPageSize) constinit CalibrationInfo g_blank_calibration_info = {};
alignas(os::MemoryPageSize) constinit SecureCalibrationInfoBackup g_secure_calibration_info_backup = {};
constinit util::optional<ams::fs::FileStorage> g_prodinfo_backup_file;
constinit util::optional<ams::fs::MemoryStorage> g_blank_prodinfo_storage;
constinit util::optional<ams::fs::MemoryStorage> g_fake_secure_backup_storage;
constinit bool g_allow_writes = false;
constinit bool g_has_secure_backup = false;
constinit os::SdkMutex g_prodinfo_management_lock;
}
void InitializeProdInfoManagement() {
std::scoped_lock lk(g_prodinfo_management_lock);
/* First, get our options. */
const bool should_blank = exosphere::ShouldBlankProdInfo();
bool allow_writes = exosphere::ShouldAllowWritesToProdInfo();
/* Next, read our prodinfo. */
ReadStorageCalibrationBinary(std::addressof(g_calibration_info));
/* Next, check if we have a secure backup. */
bool has_secure_backup = ReadStorageSecureCalibrationBinaryBackup(std::addressof(g_secure_calibration_info_backup));
/* Only allow writes if we have a secure backup. */
if (allow_writes && !has_secure_backup) {
/* If we can make a secure backup, great. */
if (IsValidForSecureBackup(g_calibration_info)) {
g_secure_calibration_info_backup.info = g_calibration_info;
WriteStorageSecureCalibrationBinaryBackup(std::addressof(g_secure_calibration_info_backup));
g_secure_calibration_info_backup.info = g_calibration_info;
has_secure_backup = true;
} else {
/* Don't allow writes if we can't make a secure backup. */
allow_writes = false;
}
}
/* Ensure our preconditions are met. */
AMS_ABORT_UNLESS(!allow_writes || has_secure_backup);
/* Set globals. */
g_allow_writes = allow_writes;
g_has_secure_backup = has_secure_backup;
/* If we should blank, do so. */
if (should_blank) {
g_blank_calibration_info = g_calibration_info;
Blank(g_blank_calibration_info);
g_blank_prodinfo_storage.emplace(std::addressof(g_blank_calibration_info), sizeof(g_blank_calibration_info));
}
/* Ensure that we have a blank file only if we need one. */
AMS_ABORT_UNLESS(should_blank == static_cast<bool>(g_blank_prodinfo_storage));
}
void SaveProdInfoBackupsAndWipeMemory(char *out_name, size_t out_name_size) {
std::scoped_lock lk(g_prodinfo_management_lock);
ON_SCOPE_EXIT {
FillWithGarbage(std::addressof(g_calibration_info), sizeof(g_calibration_info));
FillWithGarbage(std::addressof(g_secure_calibration_info_backup), sizeof(g_secure_calibration_info_backup));
};
/* Save our backup. We always prefer to save a secure copy of data over a non-secure one. */
if (g_has_secure_backup) {
GetSerialNumber(out_name, g_secure_calibration_info_backup.info);
SaveProdInfoBackup(std::addressof(g_prodinfo_backup_file), g_secure_calibration_info_backup.info);
} else {
if (IsValid(g_calibration_info) && !IsBlank(g_calibration_info)) {
GetSerialNumber(out_name, g_calibration_info);
} else {
Sha256Hash hash;
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(hash), sizeof(hash)); };
crypto::GenerateSha256(std::addressof(hash), sizeof(hash), std::addressof(g_calibration_info), sizeof(g_calibration_info));
util::SNPrintf(out_name, out_name_size, "%02X%02X%02X%02X", hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
}
SaveProdInfoBackup(std::addressof(g_prodinfo_backup_file), g_calibration_info);
}
/* Ensure we made our backup. */
AMS_ABORT_UNLESS(g_prodinfo_backup_file);
/* Setup our memory storage. */
g_fake_secure_backup_storage.emplace(std::addressof(g_secure_calibration_info_backup), sizeof(g_secure_calibration_info_backup));
/* Ensure that we have a fake storage. */
AMS_ABORT_UNLESS(static_cast<bool>(g_fake_secure_backup_storage));
}
bool ShouldReadBlankCalibrationBinary() {
std::scoped_lock lk(g_prodinfo_management_lock);
return static_cast<bool>(g_blank_prodinfo_storage);
}
bool IsWriteToCalibrationBinaryAllowed() {
std::scoped_lock lk(g_prodinfo_management_lock);
return g_allow_writes;
}
void ReadFromBlankCalibrationBinary(s64 offset, void *dst, size_t size) {
AMS_ABORT_UNLESS(ShouldReadBlankCalibrationBinary());
std::scoped_lock lk(g_prodinfo_management_lock);
R_ABORT_UNLESS(g_blank_prodinfo_storage->Read(offset, dst, size));
}
void WriteToBlankCalibrationBinary(s64 offset, const void *src, size_t size) {
AMS_ABORT_UNLESS(ShouldReadBlankCalibrationBinary());
std::scoped_lock lk(g_prodinfo_management_lock);
R_ABORT_UNLESS(g_blank_prodinfo_storage->Write(offset, src, size));
}
void ReadFromFakeSecureBackupStorage(s64 offset, void *dst, size_t size) {
AMS_ABORT_UNLESS(IsWriteToCalibrationBinaryAllowed());
std::scoped_lock lk(g_prodinfo_management_lock);
R_ABORT_UNLESS(g_fake_secure_backup_storage->Read(offset, dst, size));
}
void WriteToFakeSecureBackupStorage(s64 offset, const void *src, size_t size) {
AMS_ABORT_UNLESS(IsWriteToCalibrationBinaryAllowed());
std::scoped_lock lk(g_prodinfo_management_lock);
R_ABORT_UNLESS(g_fake_secure_backup_storage->Write(offset, src, size));
}
}

View File

@@ -1,39 +0,0 @@
/*
* 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::mitm {
constexpr inline size_t CalibrationBinarySize = 0x8000;
constexpr inline s64 SecureCalibrationInfoBackupOffset = 3_MB;
constexpr inline size_t SecureCalibrationBinaryBackupSize = 0xC000;
void InitializeProdInfoManagement();
void SaveProdInfoBackupsAndWipeMemory(char *out_name, size_t out_name_size);
bool ShouldReadBlankCalibrationBinary();
bool IsWriteToCalibrationBinaryAllowed();
void ReadFromBlankCalibrationBinary(s64 offset, void *dst, size_t size);
void WriteToBlankCalibrationBinary(s64 offset, const void *src, size_t size);
void ReadFromFakeSecureBackupStorage(s64 offset, void *dst, size_t size);
void WriteToFakeSecureBackupStorage(s64 offset, const void *src, size_t size);
}

View File

@@ -1,49 +0,0 @@
/*
* 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 "../amsmitm_initialization.hpp"
#include "bpc_ams_module.hpp"
#include "bpc_ams_service.hpp"
namespace ams::mitm::bpc_ams {
namespace {
constexpr sm::ServiceName AtmosphereServiceName = sm::ServiceName::Encode("bpc:ams");
constexpr size_t AtmosphereMaxSessions = 4;
constexpr size_t MaxServers = 1;
constexpr size_t MaxSessions = AtmosphereMaxSessions;
using ServerOptions = sf::hipc::DefaultServerManagerOptions;
sf::hipc::ServerManager<MaxServers, ServerOptions, MaxSessions> g_server_manager;
constinit sf::UnmanagedServiceObject<bpc::impl::IAtmosphereInterface, bpc::AtmosphereService> g_ams_service_object;
}
void MitmModule::ThreadFunction(void *) {
/* Create bpc:ams. */
{
os::NativeHandle bpcams_h;
R_ABORT_UNLESS(svc::ManageNamedPort(&bpcams_h, AtmosphereServiceName.name, AtmosphereMaxSessions));
g_server_manager.RegisterObjectForServer(g_ams_service_object.GetShared(), bpcams_h);
}
/* Loop forever, servicing our services. */
g_server_manager.LoopProcess();
}
}

View File

@@ -1,24 +0,0 @@
/*
* 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>
#include "../amsmitm_module.hpp"
namespace ams::mitm::bpc_ams {
DEFINE_MITM_MODULE_CLASS(0x8000, AMS_GET_SYSTEM_THREAD_PRIORITY(bpc, IpcServer));
}

View File

@@ -1,186 +0,0 @@
/*
* 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 "bpc_ams_power_utils.hpp"
#include "../amsmitm_fs_utils.hpp"
namespace ams::mitm::bpc {
namespace {
/* Convenience definitions. */
constexpr uintptr_t IramBase = 0x40000000ull;
constexpr uintptr_t IramPayloadBase = 0x40010000ull;
constexpr size_t IramSize = 0x40000;
constexpr size_t IramPayloadMaxSize = 0x24000;
constexpr size_t IramFatalErrorContextOffset = 0x2E000;
/* Helper enum. */
enum class RebootType : u32 {
Standard,
ToRcm,
ToPayload,
ByPmic,
};
/* Globals. */
alignas(os::MemoryPageSize) u8 g_work_page[os::MemoryPageSize];
alignas(os::MemoryPageSize) u8 g_reboot_payload[IramPayloadMaxSize];
RebootType g_reboot_type = RebootType::ToRcm;
/* Helpers. */
void ClearIram() {
/* Make page CCs. */
std::memset(g_work_page, 0xCC, sizeof(g_work_page));
/* Overwrite all of IRAM with CCs. */
for (size_t ofs = 0; ofs < IramSize; ofs += sizeof(g_work_page)) {
exosphere::CopyToIram(IramBase + ofs, g_work_page, sizeof(g_work_page));
}
}
void DoRebootToPayload() {
/* Ensure clean IRAM state. */
ClearIram();
/* Copy in payload. */
for (size_t ofs = 0; ofs < sizeof(g_reboot_payload); ofs += sizeof(g_work_page)) {
std::memcpy(g_work_page, g_reboot_payload + ofs, std::min(sizeof(g_reboot_payload) - ofs, sizeof(g_work_page)));
exosphere::CopyToIram(IramPayloadBase + ofs, g_work_page, sizeof(g_work_page));
}
exosphere::ForceRebootToIramPayload();
}
void DoRebootToFatalError(const ams::FatalErrorContext *ctx) {
/* Ensure clean IRAM state. */
ClearIram();
/* Copy in payload. */
for (size_t ofs = 0; ofs < sizeof(g_reboot_payload); ofs += sizeof(g_work_page)) {
std::memcpy(g_work_page, g_reboot_payload + ofs, std::min(sizeof(g_reboot_payload) - ofs, sizeof(g_work_page)));
exosphere::CopyToIram(IramPayloadBase + ofs, g_work_page, sizeof(g_work_page));
}
/* Copy in fatal error context, if relevant. */
if (ctx != nullptr) {
std::memset(g_work_page, 0xCC, sizeof(g_work_page));
std::memcpy(g_work_page, ctx, sizeof(*ctx));
exosphere::CopyToIram(IramPayloadBase + IramFatalErrorContextOffset, g_work_page, sizeof(g_work_page));
}
exosphere::ForceRebootToFatalError();
}
}
/* Power utilities. */
bool IsRebootManaged() {
return g_reboot_type != RebootType::Standard;
}
void RebootSystem() {
switch (g_reboot_type) {
case RebootType::ByPmic:
exosphere::ForceRebootByPmic();
break;
case RebootType::ToRcm:
exosphere::ForceRebootToRcm();
break;
case RebootType::ToPayload:
default: /* This should never be called with ::Standard */
DoRebootToPayload();
break;
}
}
void ShutdownSystem() {
exosphere::ForceShutdown();
}
/* Atmosphere power utilities. */
void RebootForFatalError(const ams::FatalErrorContext *ctx) {
DoRebootToFatalError(ctx);
}
void SetRebootPayload(const void *payload, size_t payload_size) {
/* Mariko does not support reboot-to-payload. */
if (spl::GetSocType() == spl::SocType_Mariko) {
return;
}
/* Clear payload buffer */
std::memset(g_reboot_payload, 0xCC, sizeof(g_reboot_payload));
/* Ensure valid. */
AMS_ABORT_UNLESS(payload != nullptr && payload_size <= sizeof(g_reboot_payload));
/* Copy in payload. */
std::memcpy(g_reboot_payload, payload, payload_size);
/* Note to the secure monitor that we have a payload. */
spl::smc::AsyncOperationKey dummy;
spl::smc::SetConfig(std::addressof(dummy), spl::ConfigItem::ExospherePayloadAddress, nullptr, 0, g_reboot_payload);
/* NOTE: Preferred reboot type may be overrwritten when parsed from settings during boot. */
g_reboot_type = RebootType::ToPayload;
}
Result LoadRebootPayload() {
/* Mariko does not support reboot-to-payload. */
R_SUCCEED_IF(spl::GetSocType() == spl::SocType_Mariko)
/* Clear payload buffer */
std::memset(g_reboot_payload, 0xCC, sizeof(g_reboot_payload));
/* Open payload file. */
FsFile payload_file;
R_TRY(fs::OpenAtmosphereSdFile(std::addressof(payload_file), "/reboot_payload.bin", ams::fs::OpenMode_Read));
ON_SCOPE_EXIT { fsFileClose(std::addressof(payload_file)); };
/* Read payload file. Discard result. */
{
size_t actual_size;
fsFileRead(std::addressof(payload_file), 0, g_reboot_payload, sizeof(g_reboot_payload), FsReadOption_None, std::addressof(actual_size));
}
/* NOTE: Preferred reboot type will be parsed from settings later on. */
g_reboot_type = RebootType::ToPayload;
R_SUCCEED();
}
Result DetectPreferredRebootFunctionality() {
char reboot_type[0x40] = {};
settings::fwdbg::GetSettingsItemValue(reboot_type, sizeof(reboot_type) - 1, "atmosphere", "power_menu_reboot_function");
if (strcasecmp(reboot_type, "stock") == 0 || strcasecmp(reboot_type, "normal") == 0 || strcasecmp(reboot_type, "standard") == 0) {
g_reboot_type = RebootType::Standard;
} else if (strcasecmp(reboot_type, "rcm") == 0) {
g_reboot_type = RebootType::ToRcm;
} else if (strcasecmp(reboot_type, "payload") == 0) {
g_reboot_type = RebootType::ToPayload;
}
/* TODO: Should we actually allow control over this on mariko? */
if (spl::GetSocType() == spl::SocType_Mariko) {
g_reboot_type = RebootType::ByPmic;
}
R_SUCCEED();
}
}

View File

@@ -1,32 +0,0 @@
/*
* 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::mitm::bpc {
/* Power utilities. */
bool IsRebootManaged();
void RebootSystem();
void ShutdownSystem();
/* Atmosphere power utilities. */
void SetRebootPayload(const void *payload, size_t payload_size);
Result LoadRebootPayload();
Result DetectPreferredRebootFunctionality();
void RebootForFatalError(const ams::FatalErrorContext *ctx);
}

View File

@@ -1,47 +0,0 @@
/*
* 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 "../amsmitm_initialization.hpp"
#include "bpc_ams_service.hpp"
#include "bpc_ams_power_utils.hpp"
namespace ams::mitm::bpc {
namespace {
bool g_set_initial_payload = false;
}
void AtmosphereService::RebootToFatalError(const ams::FatalErrorContext &ctx) {
bpc::RebootForFatalError(std::addressof(ctx));
}
void AtmosphereService::SetRebootPayload(const ams::sf::InBuffer &payload) {
/* Set the reboot payload. */
bpc::SetRebootPayload(payload.GetPointer(), payload.GetSize());
/* If this is being called for the first time (by boot sysmodule), */
/* Then we should kick off the rest of init. */
if (!g_set_initial_payload) {
g_set_initial_payload = true;
/* Start the initialization process. */
::ams::mitm::StartInitialize();
}
}
}

View File

@@ -1,34 +0,0 @@
/*
* 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>
#define AMS_BPC_MITM_ATMOSPHERE_INTERFACE_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 65000, void, RebootToFatalError, (const ams::FatalErrorContext &ctx), (ctx)) \
AMS_SF_METHOD_INFO(C, H, 65001, void, SetRebootPayload, (const ams::sf::InBuffer &payload), (payload))
AMS_SF_DEFINE_INTERFACE(ams::mitm::bpc::impl, IAtmosphereInterface, AMS_BPC_MITM_ATMOSPHERE_INTERFACE_INTERFACE_INFO, 0x00000000)
namespace ams::mitm::bpc {
class AtmosphereService {
public:
void RebootToFatalError(const ams::FatalErrorContext &ctx);
void SetRebootPayload(const ams::sf::InBuffer &payload);
};
static_assert(impl::IsIAtmosphereInterface<AtmosphereService>);
}

View File

@@ -1,33 +0,0 @@
/*
* 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 "bpc_mitm_service.hpp"
#include "bpc_ams_power_utils.hpp"
namespace ams::mitm::bpc {
Result BpcMitmService::RebootSystem() {
R_UNLESS(bpc::IsRebootManaged(), sm::mitm::ResultShouldForwardToSession());
bpc::RebootSystem();
R_SUCCEED();
}
Result BpcMitmService::ShutdownSystem() {
bpc::ShutdownSystem();
R_SUCCEED();
}
}

View File

@@ -1,49 +0,0 @@
/*
* 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>
#define AMS_BPC_MITM_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, ShutdownSystem, (), ()) \
AMS_SF_METHOD_INFO(C, H, 1, Result, RebootSystem, (), ())
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::bpc::impl, IBpcMitmInterface, AMS_BPC_MITM_INTERFACE_INFO, 0xF6C277FD)
namespace ams::mitm::bpc {
class BpcMitmService : public sf::MitmServiceImplBase {
public:
using MitmServiceImplBase::MitmServiceImplBase;
public:
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
/* We will mitm:
* - am (omm on 14.0.0+), to intercept the Reboot/Power buttons in the overlay menu.
* - fatal, to simplify payload reboot logic significantly
* - hbl, to allow homebrew to take advantage of the feature.
*/
return client_info.program_id == ncm::SystemProgramId::Am ||
client_info.program_id == ncm::SystemProgramId::Omm ||
client_info.program_id == ncm::SystemProgramId::Fatal ||
client_info.override_status.IsHbl();
}
public:
/* Overridden commands. */
Result ShutdownSystem();
Result RebootSystem();
};
static_assert(impl::IsIBpcMitmInterface<BpcMitmService>);
}

View File

@@ -1,79 +0,0 @@
/*
* 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 "../amsmitm_initialization.hpp"
#include "bpcmitm_module.hpp"
#include "bpc_mitm_service.hpp"
namespace ams::mitm::bpc {
namespace {
enum PortIndex {
PortIndex_Mitm,
PortIndex_Count,
};
constexpr sm::ServiceName MitmServiceName = sm::ServiceName::Encode("bpc");
constexpr sm::ServiceName DeprecatedMitmServiceName = sm::ServiceName::Encode("bpc:c");
constexpr size_t MitmServiceMaxSessions = 13;
constexpr size_t MaxSessions = MitmServiceMaxSessions;
struct ServerOptions {
static constexpr size_t PointerBufferSize = sf::hipc::DefaultServerManagerOptions::PointerBufferSize;
static constexpr size_t MaxDomains = sf::hipc::DefaultServerManagerOptions::MaxDomains;
static constexpr size_t MaxDomainObjects = sf::hipc::DefaultServerManagerOptions::MaxDomainObjects;
static constexpr bool CanDeferInvokeRequest = sf::hipc::DefaultServerManagerOptions::CanDeferInvokeRequest;
static constexpr bool CanManageMitmServers = true;
};
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
private:
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
};
ServerManager g_server_manager;
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
/* Acknowledge the mitm session. */
std::shared_ptr<::Service> fsrv;
sm::MitmProcessInfo client_info;
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
switch (port_index) {
case PortIndex_Mitm:
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<impl::IBpcMitmInterface, BpcMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}
void MitmModule::ThreadFunction(void *) {
/* Wait until initialization is complete. */
mitm::WaitInitialized();
/* Create bpc mitm. */
const sm::ServiceName service_name = (hos::GetVersion() >= hos::Version_2_0_0) ? MitmServiceName : DeprecatedMitmServiceName;
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<BpcMitmService>(PortIndex_Mitm, service_name)));
/* Loop forever, servicing our services. */
g_server_manager.LoopProcess();
}
}

View File

@@ -1,24 +0,0 @@
/*
* 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>
#include "../amsmitm_module.hpp"
namespace ams::mitm::bpc {
DEFINE_MITM_MODULE_CLASS(0x8000, AMS_GET_SYSTEM_THREAD_PRIORITY(bpc, IpcServer));
}

View File

@@ -1,77 +0,0 @@
/*
* 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 "../amsmitm_fs_utils.hpp"
#include "dnsmitm_debug.hpp"
namespace ams::mitm::socket::resolver {
namespace {
constinit os::SdkMutex g_log_mutex;
constinit bool g_log_enabled;
constinit ::FsFile g_log_file;
constinit s64 g_log_ofs;
constinit char g_log_buf[0x400];
}
void InitializeDebug(bool enable_log) {
{
std::scoped_lock lk(g_log_mutex);
g_log_enabled = enable_log;
if (g_log_enabled) {
/* Create the logs directory. */
mitm::fs::CreateAtmosphereSdDirectory("/logs");
/* Create the log file. */
mitm::fs::CreateAtmosphereSdFile("/logs/dns_mitm_debug.log", 0, ams::fs::CreateOption_None);
/* Open the log file. */
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(g_log_file), "/logs/dns_mitm_debug.log", ams::fs::OpenMode_ReadWrite | ams::fs::OpenMode_AllowAppend));
/* Get the current log offset. */
R_ABORT_UNLESS(::fsFileGetSize(std::addressof(g_log_file), std::addressof(g_log_ofs)));
}
}
/* Start a new log. */
LogDebug("\n---\n");
}
void LogDebug(const char *fmt, ...) {
std::scoped_lock lk(g_log_mutex);
if (g_log_enabled) {
int len = 0;
{
std::va_list vl;
va_start(vl, fmt);
len = util::VSNPrintf(g_log_buf, sizeof(g_log_buf), fmt, vl);
va_end(vl);
}
R_ABORT_UNLESS(::fsFileWrite(std::addressof(g_log_file), g_log_ofs, g_log_buf, len, FsWriteOption_Flush));
g_log_ofs += len;
}
}
}

View File

@@ -1,25 +0,0 @@
/*
* 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::mitm::socket::resolver {
void InitializeDebug(bool enable_log);
void LogDebug(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
}

View File

@@ -1,399 +0,0 @@
/*
* 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 "../amsmitm_fs_utils.hpp"
#include "dnsmitm_debug.hpp"
#include "dnsmitm_host_redirection.hpp"
#include "socket_allocator.hpp"
namespace ams::mitm::socket::resolver {
namespace {
/* https://github.com/clibs/wildcardcmp */
constexpr int wildcardcmp(const char *pattern, const char *string) {
const char *w = nullptr; /* last `*` */
const char *s = nullptr; /* last checked char */
/* malformed */
if (!pattern || !string) return 0;
/* loop 1 char at a time */
while (1) {
if (!*string) {
if (!*pattern) return 1;
if ('*' == *pattern) return 1;
if (!s || !*s) return 0;
string = s++;
pattern = w;
continue;
} else {
if (*pattern != *string) {
if ('*' == *pattern) {
w = ++pattern;
s = string;
/* "*" -> "foobar" */
if (*pattern) continue;
return 1;
} else if (w) {
string++;
/* "*ooba*" -> "foobar" */
continue;
}
return 0;
}
}
string++;
pattern++;
}
return 1;
}
constexpr const char DefaultHostsFile[] =
"# Nintendo telemetry servers\n"
"127.0.0.1 receive-%.dg.srv.nintendo.net receive-%.er.srv.nintendo.net\n";
constinit os::SdkMutex g_redirection_lock;
std::vector<std::pair<std::string, ams::socket::InAddrT>> g_redirection_list;
void RemoveRedirection(const char *hostname) {
for (auto it = g_redirection_list.begin(); it != g_redirection_list.end(); ++it) {
if (std::strcmp(it->first.c_str(), hostname) == 0) {
g_redirection_list.erase(it);
break;
}
}
}
void AddRedirection(const char *hostname, ams::socket::InAddrT addr) {
RemoveRedirection(hostname);
g_redirection_list.emplace(g_redirection_list.begin(), std::string(hostname), addr);
}
constinit char g_specific_emummc_hosts_path[0x40] = {};
void ParseHostsFile(const char *file_data) {
/* Get the environment identifier from settings. */
const auto env = ams::nsd::impl::device::GetEnvironmentIdentifierFromSettings();
const auto env_len = std::strlen(env.value);
/* Parse the file. */
enum class State {
IgnoredLine,
BeginLine,
Ip1,
IpDot1,
Ip2,
IpDot2,
Ip3,
IpDot3,
Ip4,
WhiteSpace,
HostName,
};
ams::socket::InAddrT current_address{};
char current_hostname[0x200];
u32 work{};
State state = State::BeginLine;
for (const char *cur = file_data; *cur != '\x00'; ++cur) {
const char c = *cur;
switch (state) {
case State::IgnoredLine:
if (c == '\n') {
state = State::BeginLine;
}
break;
case State::BeginLine:
if (std::isdigit(static_cast<unsigned char>(c))) {
current_address = 0;
work = static_cast<u32>(c - '0');
state = State::Ip1;
} else if (c == '\n') {
state = State::BeginLine;
} else {
state = State::IgnoredLine;
}
break;
case State::Ip1:
if (std::isdigit(static_cast<unsigned char>(c))) {
work *= 10;
work += static_cast<u32>(c - '0');
} else if (c == '.') {
current_address |= (work & 0xFF) << 0;
work = 0;
state = State::IpDot1;
} else {
state = State::IgnoredLine;
}
break;
case State::IpDot1:
if (std::isdigit(static_cast<unsigned char>(c))) {
work = static_cast<u32>(c - '0');
state = State::Ip2;
} else {
state = State::IgnoredLine;
}
break;
case State::Ip2:
if (std::isdigit(static_cast<unsigned char>(c))) {
work *= 10;
work += static_cast<u32>(c - '0');
} else if (c == '.') {
current_address |= (work & 0xFF) << 8;
work = 0;
state = State::IpDot2;
} else {
state = State::IgnoredLine;
}
break;
case State::IpDot2:
if (std::isdigit(static_cast<unsigned char>(c))) {
work = static_cast<u32>(c - '0');
state = State::Ip3;
} else {
state = State::IgnoredLine;
}
break;
case State::Ip3:
if (std::isdigit(static_cast<unsigned char>(c))) {
work *= 10;
work += static_cast<u32>(c - '0');
} else if (c == '.') {
current_address |= (work & 0xFF) << 16;
work = 0;
state = State::IpDot3;
} else {
state = State::IgnoredLine;
}
break;
case State::IpDot3:
if (std::isdigit(static_cast<unsigned char>(c))) {
work = static_cast<u32>(c - '0');
state = State::Ip4;
} else {
state = State::IgnoredLine;
}
break;
case State::Ip4:
if (std::isdigit(static_cast<unsigned char>(c))) {
work *= 10;
work += static_cast<u32>(c - '0');
} else if (c == ' ' || c == '\t') {
current_address |= (work & 0xFF) << 24;
work = 0;
state = State::WhiteSpace;
} else {
state = State::IgnoredLine;
}
break;
case State::WhiteSpace:
if (c == '\n') {
state = State::BeginLine;
} else if (c != ' ' && c != '\r' && c != '\t') {
if (c == '%') {
std::memcpy(current_hostname, env.value, env_len);
work = env_len;
} else {
current_hostname[0] = c;
work = 1;
}
state = State::HostName;
}
break;
case State::HostName:
if (c == ' ' || c == '\r' || c == '\n' || c == '\t') {
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
current_hostname[work] = '\x00';
AddRedirection(current_hostname, current_address);
work = 0;
if (c == '\n') {
state = State::BeginLine;
} else {
state = State::WhiteSpace;
}
} else if (c == '%') {
AMS_ABORT_UNLESS(work < sizeof(current_hostname) - env_len);
std::memcpy(current_hostname + work, env.value, env_len);
work += env_len;
} else {
AMS_ABORT_UNLESS(work < sizeof(current_hostname) - 1);
current_hostname[work++] = c;
}
}
}
if (state == State::HostName) {
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
current_hostname[work] = '\x00';
AddRedirection(current_hostname, current_address);
}
}
void Log(::FsFile &f, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
void Log(::FsFile &f, const char *fmt, ...) {
char log_buf[0x100];
int len = 0;
{
std::va_list vl;
va_start(vl, fmt);
len = util::VSNPrintf(log_buf, sizeof(log_buf), fmt, vl);
va_end(vl);
}
s64 ofs;
R_ABORT_UNLESS(::fsFileGetSize(std::addressof(f), std::addressof(ofs)));
R_ABORT_UNLESS(::fsFileWrite(std::addressof(f), ofs, log_buf, len, FsWriteOption_Flush));
}
const char *SelectHostsFile(::FsFile &log_file) {
Log(log_file, "Selecting hosts file...\n");
const bool is_emummc = emummc::IsActive();
const u32 emummc_id = emummc::GetActiveId();
util::SNPrintf(g_specific_emummc_hosts_path, sizeof(g_specific_emummc_hosts_path), "/hosts/emummc_%04x.txt", emummc_id);
if (is_emummc) {
if (mitm::fs::HasAtmosphereSdFile(g_specific_emummc_hosts_path)) {
return g_specific_emummc_hosts_path;
}
Log(log_file, "Skipping %s because it does not exist...\n", g_specific_emummc_hosts_path);
if (mitm::fs::HasAtmosphereSdFile("/hosts/emummc.txt")) {
return "/hosts/emummc.txt";
}
Log(log_file, "Skipping %s because it does not exist...\n", "/hosts/emummc.txt");
} else {
if (mitm::fs::HasAtmosphereSdFile("/hosts/sysmmc.txt")) {
return "/hosts/sysmmc.txt";
}
Log(log_file, "Skipping %s because it does not exist...\n", "/hosts/sysmmc.txt");
}
return "/hosts/default.txt";
}
bool ShouldAddDefaultResolverRedirections() {
u8 en = 0;
if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "add_defaults_to_dns_hosts") == sizeof(en)) {
return (en != 0);
}
return false;
}
}
void InitializeResolverRedirections() {
/* Get whether we should add defaults. */
const bool add_defaults = ShouldAddDefaultResolverRedirections();
/* Acquire exclusive access to the map. */
std::scoped_lock lk(g_redirection_lock);
/* Clear the redirections map. */
g_redirection_list.clear();
/* Open log file. */
::FsFile log_file;
mitm::fs::DeleteAtmosphereSdFile("/logs/dns_mitm_startup.log");
mitm::fs::CreateAtmosphereSdDirectory("/logs");
R_ABORT_UNLESS(mitm::fs::CreateAtmosphereSdFile("/logs/dns_mitm_startup.log", 0, ams::fs::CreateOption_None));
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(log_file), "/logs/dns_mitm_startup.log", ams::fs::OpenMode_ReadWrite | ams::fs::OpenMode_AllowAppend));
ON_SCOPE_EXIT { ::fsFileClose(std::addressof(log_file)); };
Log(log_file, "DNS Mitm:\n");
/* If a default hosts file doesn't exist on the sd card, create one. */
if (!mitm::fs::HasAtmosphereSdFile("/hosts/default.txt")) {
Log(log_file, "Creating /hosts/default.txt because it does not exist.\n");
mitm::fs::CreateAtmosphereSdDirectory("/hosts");
R_ABORT_UNLESS(mitm::fs::CreateAtmosphereSdFile("/hosts/default.txt", sizeof(DefaultHostsFile) - 1, ams::fs::CreateOption_None));
::FsFile default_file;
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(default_file), "/hosts/default.txt", ams::fs::OpenMode_ReadWrite));
R_ABORT_UNLESS(::fsFileWrite(std::addressof(default_file), 0, DefaultHostsFile, sizeof(DefaultHostsFile) - 1, ::FsWriteOption_Flush));
::fsFileClose(std::addressof(default_file));
}
/* If we should, add the defaults. */
if (add_defaults) {
Log(log_file, "Adding defaults to redirection list.\n");
ParseHostsFile(DefaultHostsFile);
}
/* Select the hosts file. */
const char *hosts_path = SelectHostsFile(log_file);
Log(log_file, "Selected %s\n", hosts_path);
/* Load the hosts file. */
{
char *hosts_file_data = nullptr;
ON_SCOPE_EXIT { if (hosts_file_data != nullptr) { ams::Free(hosts_file_data); } };
{
::FsFile hosts_file;
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(hosts_file), hosts_path, ams::fs::OpenMode_Read));
ON_SCOPE_EXIT { ::fsFileClose(std::addressof(hosts_file)); };
/* Get the hosts file size. */
s64 hosts_size;
R_ABORT_UNLESS(::fsFileGetSize(std::addressof(hosts_file), std::addressof(hosts_size)));
/* Validate we can read the file. */
AMS_ABORT_UNLESS(0 <= hosts_size && hosts_size < 0x8000);
/* Read the data. */
hosts_file_data = static_cast<char *>(ams::Malloc(0x8000));
AMS_ABORT_UNLESS(hosts_file_data != nullptr);
u64 br;
R_ABORT_UNLESS(::fsFileRead(std::addressof(hosts_file), 0, hosts_file_data, hosts_size, ::FsReadOption_None, std::addressof(br)));
AMS_ABORT_UNLESS(br == static_cast<u64>(hosts_size));
/* Null-terminate. */
hosts_file_data[hosts_size] = '\x00';
}
/* Parse the hosts file. */
ParseHostsFile(hosts_file_data);
}
/* Print the redirections. */
Log(log_file, "Redirections:\n");
for (const auto &[host, address] : g_redirection_list) {
Log(log_file, " `%s` -> %u.%u.%u.%u\n", host.c_str(), (address >> 0) & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF, (address >> 24) & 0xFF);
}
}
bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname) {
std::scoped_lock lk(g_redirection_lock);
for (const auto &[host, address] : g_redirection_list) {
if (wildcardcmp(host.c_str(), hostname)) {
*out = address;
return true;
}
}
return false;
}
}

View File

@@ -1,25 +0,0 @@
/*
* 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::mitm::socket::resolver {
void InitializeResolverRedirections();
bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname);
}

View File

@@ -1,149 +0,0 @@
/*
* 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 "../amsmitm_initialization.hpp"
#include "dnsmitm_module.hpp"
#include "dnsmitm_debug.hpp"
#include "dnsmitm_resolver_impl.hpp"
#include "dnsmitm_host_redirection.hpp"
namespace ams::mitm::socket::resolver {
namespace {
enum PortIndex {
PortIndex_Mitm,
PortIndex_Count,
};
constexpr sm::ServiceName DnsMitmServiceName = sm::ServiceName::Encode("sfdnsres");
constexpr size_t MaxSessions = 30;
struct ServerOptions {
static constexpr size_t PointerBufferSize = sf::hipc::DefaultServerManagerOptions::PointerBufferSize;
static constexpr size_t MaxDomains = sf::hipc::DefaultServerManagerOptions::MaxDomains;
static constexpr size_t MaxDomainObjects = sf::hipc::DefaultServerManagerOptions::MaxDomainObjects;
static constexpr bool CanDeferInvokeRequest = sf::hipc::DefaultServerManagerOptions::CanDeferInvokeRequest;
static constexpr bool CanManageMitmServers = true;
};
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
private:
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
};
alignas(os::MemoryPageSize) constinit u8 g_resolver_allocator_buffer[16_KB];
ServerManager g_server_manager;
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
/* Acknowledge the mitm session. */
std::shared_ptr<::Service> fsrv;
sm::MitmProcessInfo client_info;
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
switch (port_index) {
case PortIndex_Mitm:
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<IResolver, ResolverImpl>(decltype(fsrv)(fsrv), client_info), fsrv));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
constexpr size_t TotalThreads = 8;
static_assert(TotalThreads >= 1, "TotalThreads");
constexpr size_t NumExtraThreads = TotalThreads - 1;
constexpr size_t ThreadStackSize = mitm::ModuleTraits<socket::resolver::MitmModule>::StackSize;
alignas(os::MemoryPageSize) u8 g_extra_thread_stacks[NumExtraThreads][ThreadStackSize];
os::ThreadType g_extra_threads[NumExtraThreads];
void LoopServerThread(void *) {
/* Loop forever, servicing our services. */
g_server_manager.LoopProcess();
}
void ProcessForServerOnAllThreads() {
/* Initialize threads. */
if constexpr (NumExtraThreads > 0) {
const s32 priority = os::GetThreadCurrentPriority(os::GetCurrentThread());
for (size_t i = 0; i < NumExtraThreads; i++) {
R_ABORT_UNLESS(os::CreateThread(g_extra_threads + i, LoopServerThread, nullptr, g_extra_thread_stacks[i], ThreadStackSize, priority));
}
}
/* Start extra threads. */
if constexpr (NumExtraThreads > 0) {
for (size_t i = 0; i < NumExtraThreads; i++) {
os::StartThread(g_extra_threads + i);
}
}
/* Loop this thread. */
LoopServerThread(nullptr);
/* Wait for extra threads to finish. */
if constexpr (NumExtraThreads > 0) {
for (size_t i = 0; i < NumExtraThreads; i++) {
os::WaitThread(g_extra_threads + i);
}
}
}
bool ShouldMitmDns() {
u8 en = 0;
if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_dns_mitm") == sizeof(en)) {
return (en != 0);
}
return false;
}
bool ShouldEnableDebugLog() {
u8 en = 0;
if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_dns_mitm_debug_log") == sizeof(en)) {
return (en != 0);
}
return false;
}
}
void MitmModule::ThreadFunction(void *) {
/* Wait until initialization is complete. */
mitm::WaitInitialized();
/* If we shouldn't mitm dns, don't do anything at all. */
if (!ShouldMitmDns()) {
return;
}
/* Initialize the socket allocator. */
ams::socket::InitializeAllocatorForInternal(g_resolver_allocator_buffer, sizeof(g_resolver_allocator_buffer));
/* Initialize debug. */
resolver::InitializeDebug(ShouldEnableDebugLog());
/* Initialize redirection map. */
resolver::InitializeResolverRedirections();
/* Create mitm servers. */
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<ResolverImpl>(PortIndex_Mitm, DnsMitmServiceName)));
/* Loop forever, servicing our services. */
ProcessForServerOnAllThreads();
}
}

View File

@@ -1,24 +0,0 @@
/*
* 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>
#include "../amsmitm_module.hpp"
namespace ams::mitm::socket::resolver {
DEFINE_MITM_MODULE_CLASS(0x2000, AMS_GET_SYSTEM_THREAD_PRIORITY(socket, ResolverIpcServer) - 1);
}

View File

@@ -1,218 +0,0 @@
/*
* 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 "dnsmitm_resolver_impl.hpp"
#include "dnsmitm_debug.hpp"
#include "dnsmitm_host_redirection.hpp"
#include "serializer/serializer.hpp"
#include "sfdnsres_shim.h"
namespace ams::mitm::socket::resolver {
ssize_t SerializeRedirectedHostEnt(u8 * const dst, size_t dst_size, const char *hostname, ams::socket::InAddrT redirect_addr) {
struct in_addr addr = { .s_addr = redirect_addr };
struct in_addr *addr_list[2] = { std::addressof(addr), nullptr };
struct hostent ent = {
.h_name = const_cast<char *>(hostname),
.h_aliases = nullptr,
.h_addrtype = AF_INET,
.h_length = sizeof(u32),
.h_addr_list = (char **)addr_list,
};
const auto result = serializer::DNSSerializer::ToBuffer(dst, dst_size, ent);
AMS_ABORT_UNLESS(result >= 0);
return result;
}
ssize_t SerializeRedirectedAddrInfo(u8 * const dst, size_t dst_size, const char *hostname, ams::socket::InAddrT redirect_addr, u16 redirect_port, const struct addrinfo *hint) {
AMS_UNUSED(hostname);
struct addrinfo ai = {
.ai_flags = 0,
.ai_family = AF_UNSPEC,
.ai_socktype = 0,
.ai_protocol = 0,
.ai_addrlen = 0,
.ai_canonname = nullptr,
.ai_next = nullptr,
};
if (hint != nullptr) {
ai = *hint;
}
switch (ai.ai_family) {
case AF_UNSPEC: ai.ai_family = AF_INET; break;
case AF_INET: ai.ai_family = AF_INET; break;
case AF_INET6: AMS_ABORT("Redirected INET6 not supported"); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
if (ai.ai_socktype == 0) {
ai.ai_socktype = SOCK_STREAM;
}
if (ai.ai_protocol == 0) {
ai.ai_protocol = IPPROTO_TCP;
}
const struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_port = ams::socket::InetHtons(redirect_port),
.sin_addr = { .s_addr = redirect_addr },
.sin_zero = {},
};
ai.ai_addrlen = sizeof(sin);
ai.ai_addr = (struct sockaddr *)(std::addressof(sin));
const auto result = serializer::DNSSerializer::ToBuffer(dst, dst_size, ai);
AMS_ABORT_UNLESS(result >= 0);
return result;
}
Result ResolverImpl::GetHostByNameRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out<u32> out_host_error, sf::Out<u32> out_errno, const sf::OutBuffer &out_hostent, sf::Out<u32> out_size) {
AMS_UNUSED(cancel_handle, client_pid, use_nsd_resolve);
const char *hostname = reinterpret_cast<const char *>(name.GetPointer());
LogDebug("[%016lx]: GetHostByNameRequest(%s)\n", m_client_info.program_id.value, hostname);
R_UNLESS(hostname != nullptr, sm::mitm::ResultShouldForwardToSession());
ams::socket::InAddrT redirect_addr = {};
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
LogDebug("[%016lx]: Redirecting %s to %u.%u.%u.%u\n", m_client_info.program_id.value, hostname, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
const auto size = SerializeRedirectedHostEnt(out_hostent.GetPointer(), out_hostent.GetSize(), hostname, redirect_addr);
*out_host_error = 0;
*out_errno = 0;
*out_size = size;
R_SUCCEED();
}
Result ResolverImpl::GetAddrInfoRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out<u32> out_errno, sf::Out<s32> out_retval, sf::Out<u32> out_size) {
AMS_UNUSED(cancel_handle, client_pid, use_nsd_resolve);
const char *hostname = reinterpret_cast<const char *>(node.GetPointer());
LogDebug("[%016lx]: GetAddrInfoRequest(%s, %s)\n", m_client_info.program_id.value, reinterpret_cast<const char *>(node.GetPointer()), reinterpret_cast<const char *>(srv.GetPointer()));
R_UNLESS(hostname != nullptr, sm::mitm::ResultShouldForwardToSession());
ams::socket::InAddrT redirect_addr = {};
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
u16 port = 0;
if (srv.GetPointer() != nullptr) {
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
port *= 10;
port += *cur - '0';
}
}
LogDebug("[%016lx]: Redirecting %s:%u to %u.%u.%u.%u\n", m_client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
const bool use_hint = serialized_hint.GetPointer() != nullptr;
struct addrinfo hint = {};
if (use_hint) {
AMS_ABORT_UNLESS(serializer::DNSSerializer::FromBuffer(hint, serialized_hint.GetPointer(), serialized_hint.GetSize()) >= 0);
}
ON_SCOPE_EXIT { if (use_hint) { serializer::FreeAddrInfo(hint); } };
const auto size = SerializeRedirectedAddrInfo(out_addrinfo.GetPointer(), out_addrinfo.GetSize(), hostname, redirect_addr, port, use_hint ? std::addressof(hint) : nullptr);
*out_retval = 0;
*out_errno = 0;
*out_size = size;
R_SUCCEED();
}
Result ResolverImpl::GetHostByNameRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out<u32> out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno) {
AMS_UNUSED(client_pid, options_version, options, num_options);
const char *hostname = reinterpret_cast<const char *>(name.GetPointer());
LogDebug("[%016lx]: GetHostByNameRequestWithOptions(%s)\n", m_client_info.program_id.value, hostname);
R_UNLESS(hostname != nullptr, sm::mitm::ResultShouldForwardToSession());
ams::socket::InAddrT redirect_addr = {};
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
LogDebug("[%016lx]: Redirecting %s to %u.%u.%u.%u\n", m_client_info.program_id.value, hostname, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
const auto size = SerializeRedirectedHostEnt(out_hostent.GetPointer(), out_hostent.GetSize(), hostname, redirect_addr);
*out_host_error = 0;
*out_errno = 0;
*out_size = size;
R_SUCCEED();
}
Result ResolverImpl::GetAddrInfoRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out<u32> out_size, sf::Out<s32> out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno) {
AMS_UNUSED(client_pid, options_version, options, num_options);
const char *hostname = reinterpret_cast<const char *>(node.GetPointer());
LogDebug("[%016lx]: GetAddrInfoRequestWithOptions(%s, %s)\n", m_client_info.program_id.value, hostname, reinterpret_cast<const char *>(srv.GetPointer()));
R_UNLESS(hostname != nullptr, sm::mitm::ResultShouldForwardToSession());
ams::socket::InAddrT redirect_addr = {};
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
u16 port = 0;
if (srv.GetPointer() != nullptr) {
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
port *= 10;
port += *cur - '0';
}
}
LogDebug("[%016lx]: Redirecting %s:%u to %u.%u.%u.%u\n", m_client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
const bool use_hint = serialized_hint.GetPointer() != nullptr;
struct addrinfo hint = {};
if (use_hint) {
AMS_ABORT_UNLESS(serializer::DNSSerializer::FromBuffer(hint, serialized_hint.GetPointer(), serialized_hint.GetSize()) >= 0);
}
ON_SCOPE_EXIT { if (use_hint) { serializer::FreeAddrInfo(hint); } };
const auto size = SerializeRedirectedAddrInfo(out_addrinfo.GetPointer(), out_addrinfo.GetSize(), hostname, redirect_addr, port, use_hint ? std::addressof(hint) : nullptr);
*out_retval = 0;
*out_host_error = 0;
*out_errno = 0;
*out_size = size;
R_SUCCEED();
}
Result ResolverImpl::AtmosphereReloadHostsFile() {
/* Perform a hosts file reload. */
InitializeResolverRedirections();
R_SUCCEED();
}
}

View File

@@ -1,53 +0,0 @@
/*
* 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>
#define AMS_DNS_MITM_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 2, Result, GetHostByNameRequest, (u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out<u32> out_host_error, sf::Out<u32> out_errno, const sf::OutBuffer &out_hostent, sf::Out<u32> out_size), (cancel_handle, client_pid, use_nsd_resolve, name, out_host_error, out_errno, out_hostent, out_size)) \
AMS_SF_METHOD_INFO(C, H, 6, Result, GetAddrInfoRequest, (u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out<u32> out_errno, sf::Out<s32> out_retval, sf::Out<u32> out_size), (cancel_handle, client_pid, use_nsd_resolve, node, srv, serialized_hint, out_addrinfo, out_errno, out_retval, out_size)) \
AMS_SF_METHOD_INFO(C, H, 10, Result, GetHostByNameRequestWithOptions, (const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out<u32> out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno), (client_pid, name, out_hostent, out_size, options_version, options, num_options, out_host_error, out_errno), hos::Version_5_0_0) \
AMS_SF_METHOD_INFO(C, H, 12, Result, GetAddrInfoRequestWithOptions, (const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out<u32> out_size, sf::Out<s32> out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno), (client_pid, node, srv, serialized_hint, out_addrinfo, out_size, out_retval, options_version, options, num_options, out_host_error, out_errno), hos::Version_5_0_0) \
AMS_SF_METHOD_INFO(C, H, 65000, Result, AtmosphereReloadHostsFile, (), ())
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::socket::resolver, IResolver, AMS_DNS_MITM_INTERFACE_INFO, 0x5935F91A)
namespace ams::mitm::socket::resolver {
class ResolverImpl : public sf::MitmServiceImplBase {
public:
using MitmServiceImplBase::MitmServiceImplBase;
public:
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
/* We will mitm:
* - everything.
*/
AMS_UNUSED(client_info);
return true;
}
public:
/* Overridden commands. */
Result GetHostByNameRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out<u32> out_host_error, sf::Out<u32> out_errno, const sf::OutBuffer &out_hostent, sf::Out<u32> out_size);
Result GetAddrInfoRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out<u32> out_errno, sf::Out<s32> out_retval, sf::Out<u32> out_size);
Result GetHostByNameRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out<u32> out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno);
Result GetAddrInfoRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out<u32> out_size, sf::Out<s32> out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno);
/* Extension commands. */
Result AtmosphereReloadHostsFile();
};
static_assert(IsIResolver<ResolverImpl>);
}

View File

@@ -1,52 +0,0 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
ssize_t DNSSerializer::CheckToBufferArguments(const u8 *dst, size_t dst_size, size_t required, int error_id) {
/* TODO: Logging, using error_id */
AMS_UNUSED(error_id);
if (dst == nullptr) {
return -1;
} else if (dst_size < required) {
return -1;
}
return 0;
}
u32 DNSSerializer::InternalHton(const u32 &v) {
return ams::socket::InetHtonl(v);
}
u16 DNSSerializer::InternalHton(const u16 &v) {
return ams::socket::InetHtons(v);
}
u32 DNSSerializer::InternalNtoh(const u32 &v) {
return ams::socket::InetNtohl(v);
}
u16 DNSSerializer::InternalNtoh(const u16 &v) {
return ams::socket::InetNtohs(v);
}
}

View File

@@ -1,73 +0,0 @@
/*
* 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::mitm::socket::resolver::serializer {
class DNSSerializer {
public:
static ssize_t CheckToBufferArguments(const u8 *dst, size_t dst_size, size_t required, int error_id);
static u32 InternalHton(const u32 &v);
static u16 InternalHton(const u16 &v);
static u32 InternalNtoh(const u32 &v);
static u16 InternalNtoh(const u16 &v);
public:
template<typename T>
static size_t SizeOf(const T &in);
template<typename T>
static size_t SizeOf(const T *in);
template<typename T>
static size_t SizeOf(const T *in, size_t count);
template<typename T>
static size_t SizeOf(const T **arr, u32 &out_count);
template<typename T>
static ssize_t ToBuffer(u8 * const dst, size_t dst_size, const T &in);
template<typename T>
static ssize_t FromBuffer(T &out, const u8 *src, size_t src_size);
template<typename T>
static ssize_t ToBuffer(u8 * const dst, size_t dst_Size, T *in);
template<typename T>
static ssize_t ToBuffer(u8 * const dst, size_t dst_size, T **arr);
template<typename T>
static ssize_t FromBuffer(T *&out, const u8 *src, size_t src_size);
template<typename T>
static ssize_t FromBuffer(T **&out_arr, const u8 *src, size_t src_size);
template<typename T>
static ssize_t ToBuffer(u8 * const dst, size_t dst_size, const T * const arr, size_t count);
template<typename T>
static ssize_t FromBuffer(T * const arr, size_t arr_size, const u8 *src, size_t src_size, size_t count);
};
void FreeHostent(ams::socket::HostEnt &ent);
void FreeHostent(struct hostent &ent);
void FreeAddrInfo(ams::socket::AddrInfo &addr_info);
void FreeAddrInfo(struct addrinfo &addr_info);
}

View File

@@ -1,444 +0,0 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
namespace {
constexpr inline u32 AddrInfoMagic = 0xBEEFCAFE;
template<typename T>
concept IsAddrInfo = std::same_as<T, ams::socket::AddrInfo> || std::same_as<T, struct addrinfo>;
template<typename T> requires IsAddrInfo<T>
using SockAddrType = typename std::conditional<std::same_as<T, ams::socket::AddrInfo>, ams::socket::SockAddr, struct sockaddr>::type;
template<typename T> requires IsAddrInfo<T>
using SockAddrInType = typename std::conditional<std::same_as<T, ams::socket::AddrInfo>, ams::socket::SockAddrIn, struct sockaddr_in>::type;
template<typename T> requires IsAddrInfo<T>
using SockAddrIn6Type = typename std::conditional<std::same_as<T, ams::socket::AddrInfo>, struct sockaddr_in6, struct sockaddr_in6>::type;
template<typename T> requires IsAddrInfo<T>
constexpr bool IsAfInet(const auto ai_family) {
if constexpr (std::same_as<T, ams::socket::AddrInfo>) {
return ai_family == ams::socket::Family::Af_Inet;
} else {
return ai_family == AF_INET;
}
}
template<typename T> requires IsAddrInfo<T>
constexpr bool IsAfInet6(const auto ai_family) {
if constexpr (std::same_as<T, ams::socket::AddrInfo>) {
return ai_family == ams::socket::Family::Af_Inet6;
} else {
return ai_family == AF_INET;
}
}
template<typename T> requires IsAddrInfo<T>
void FreeAddrInfoImpl(T &addr_info) {
T *next = nullptr;
for (T *cur = std::addressof(addr_info); cur != nullptr; cur = next) {
next = cur->ai_next;
if (cur->ai_addr != nullptr) {
if (IsAfInet<T>(cur->ai_family)) {
ams::socket::impl::Free(reinterpret_cast<SockAddrInType<T> *>(cur->ai_addr));
} else if (IsAfInet6<T>(cur->ai_family)) {
ams::socket::impl::Free(reinterpret_cast<SockAddrIn6Type<T> *>(cur->ai_addr));
} else {
ams::socket::impl::Free(cur->ai_addr);
}
cur->ai_addr = nullptr;
}
if (cur->ai_canonname != nullptr) {
ams::socket::impl::Free(cur->ai_canonname);
cur->ai_canonname = nullptr;
}
if (cur != std::addressof(addr_info)) {
ams::socket::impl::Free(cur);
}
}
}
template<typename T> requires IsAddrInfo<T>
size_t AddrInfoSingleSizeOf(const T *addr_info) {
size_t rc = 6 * sizeof(u32);
if (addr_info->ai_addr == nullptr) {
rc += sizeof(u32);
} else if (IsAfInet<T>(addr_info->ai_family)) {
rc += DNSSerializer::SizeOf(*reinterpret_cast<SockAddrInType<T> *>(addr_info->ai_addr));
} else if (IsAfInet6<T>(addr_info->ai_family)) {
rc += DNSSerializer::SizeOf(*reinterpret_cast<SockAddrIn6Type<T> *>(addr_info->ai_addr));
} else if (addr_info->ai_addrlen == 0) {
rc += sizeof(u32);
} else {
rc += addr_info->ai_addrlen;
}
if (addr_info->ai_canonname != nullptr) {
rc += DNSSerializer::SizeOf(static_cast<const char *>(addr_info->ai_canonname));
} else {
rc += sizeof(u8);
}
if (addr_info->ai_next == nullptr) {
rc += sizeof(u32);
}
return rc;
}
template<typename T> requires IsAddrInfo<T>
size_t SizeOfImpl(const T &in) {
size_t rc = 0;
for (const T *addr_info = std::addressof(in); addr_info != nullptr; addr_info = addr_info->ai_next) {
rc += AddrInfoSingleSizeOf(addr_info);
}
return rc;
}
template<typename T> requires IsAddrInfo<T>
ssize_t ToBufferInternalImpl(u8 * const dst, size_t dst_size, const T &addr_info) {
ssize_t rc = -1;
u8 *cur = dst;
{
const u32 value = AddrInfoMagic;
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
const u32 value = static_cast<u32>(addr_info.ai_flags);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
const u32 value = static_cast<u32>(addr_info.ai_family);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
const u32 value = static_cast<u32>(addr_info.ai_socktype);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
const u32 value = static_cast<u32>(addr_info.ai_protocol);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
const u32 value = static_cast<u32>(addr_info.ai_addrlen);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
if (addr_info.ai_addr == nullptr || addr_info.ai_addrlen == 0) {
const u32 value = 0;
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
} else if (IsAfInet<T>(addr_info.ai_family)) {
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), *reinterpret_cast<SockAddrInType<T> *>(addr_info.ai_addr))) == -1) {
return rc;
}
} else if (IsAfInet6<T>(addr_info.ai_family)) {
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), *reinterpret_cast<SockAddrIn6Type<T> *>(addr_info.ai_addr))) == -1) {
return rc;
}
} else {
if (dst_size - (cur - dst) < addr_info.ai_addrlen) {
rc = -1;
return rc;
}
/* NOTE: This is clearly a nintendo bug, see the accompanying note in FromBufferInternalImpl */
std::memmove(cur, std::addressof(addr_info.ai_addr), addr_info.ai_addrlen);
rc = addr_info.ai_addrlen;
}
cur += rc;
}
{
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), addr_info.ai_canonname)) == -1) {
return rc;
}
cur += rc;
}
if (addr_info.ai_next == nullptr) {
const u32 value = 0;
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
rc = cur - dst;
return rc;
}
template<typename T> requires IsAddrInfo<T>
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
ssize_t rc = -1;
u8 *cur = dst;
std::memset(dst, 0, dst_size);
const size_t required = DNSSerializer::SizeOf(in);
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, required, __LINE__)) == -1) {
return rc;
}
for (const T *addr_info = std::addressof(in); addr_info != nullptr; addr_info = addr_info->ai_next) {
if ((rc = ToBufferInternalImpl(cur, dst_size, *addr_info)) == -1) {
return rc;
}
cur += rc;
}
rc = cur - dst;
return rc;
}
template<typename T> requires IsAddrInfo<T>
ssize_t FromBufferInternalImpl(T &out, const u8 *src, size_t src_size) {
ssize_t rc = -1;
const u8 *cur = src;
std::memset(std::addressof(out), 0, sizeof(out));
ON_SCOPE_EXIT { if (rc < 0) { FreeAddrInfo(out); } };
u32 tmp_value;
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
} else if (tmp_value != AddrInfoMagic) {
return rc;
}
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.ai_flags = static_cast<decltype(out.ai_flags)>(tmp_value);
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.ai_family = static_cast<decltype(out.ai_family)>(tmp_value);
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.ai_socktype = static_cast<decltype(out.ai_socktype)>(tmp_value);
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.ai_protocol = static_cast<decltype(out.ai_protocol)>(tmp_value);
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.ai_addrlen = static_cast<decltype(out.ai_addrlen)>(tmp_value);
cur += rc;
}
{
if (out.ai_addrlen == 0) {
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
if (tmp_value != 0) {
rc = -1;
return rc;
}
out.ai_addr = nullptr;
} else if (IsAfInet<T>(out.ai_family)) {
out.ai_addr = static_cast<SockAddrType<T> *>(ams::socket::impl::Alloc(sizeof(SockAddrInType<T>)));
if (out.ai_addr == nullptr) {
rc = -1;
return rc;
}
std::memset(out.ai_addr, 0, sizeof(SockAddrInType<T>));
if ((rc = DNSSerializer::FromBuffer(*reinterpret_cast<SockAddrInType<T> *>(out.ai_addr), cur, src_size - (cur - src))) == -1) {
return rc;
}
} else if (IsAfInet6<T>(out.ai_family)) {
out.ai_addr = static_cast<SockAddrType<T> *>(ams::socket::impl::Alloc(sizeof(SockAddrIn6Type<T>)));
if (out.ai_addr == nullptr) {
rc = -1;
return rc;
}
std::memset(out.ai_addr, 0, sizeof(SockAddrIn6Type<T>));
if ((rc = DNSSerializer::FromBuffer(*reinterpret_cast<SockAddrIn6Type<T> *>(out.ai_addr), cur, src_size - (cur - src))) == -1) {
return rc;
}
} else {
out.ai_addr = static_cast<decltype(out.ai_addr)>(ams::socket::impl::Alloc(out.ai_addrlen));
if (out.ai_addr == nullptr) {
rc = -1;
return rc;
}
/* NOTE: This is *clearly* a nintendo bug. */
/* They obviously intend to copy to the buffer they just allocated, but instead they copy to the addrinfo structure itself. */
/* Probably &out.ai_addr instead of &out.ai_addr[0]? Either way, we'll implement what they do, but... */
std::memcpy(std::addressof(out.ai_addr), cur, out.ai_addrlen);
rc = out.ai_addrlen;
}
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(out.ai_canonname, cur, src_size - (cur - src))) == -1) {
return rc;
}
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
} else if (tmp_value == 0) {
out.ai_next = nullptr;
cur += rc;
} else if (tmp_value == AddrInfoMagic) {
out.ai_next = static_cast<T *>(ams::socket::impl::Alloc(sizeof(T)));
if (out.ai_next == nullptr) {
rc = -1;
return rc;
}
std::memset(out.ai_next, 0, sizeof(T));
} else {
rc = -1;
return rc;
}
}
rc = cur - src;
return rc;
}
template<typename T> requires IsAddrInfo<T>
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
ssize_t rc = 0;
const u8 *cur = src;
const size_t required = DNSSerializer::SizeOf(out);
if (src_size < required) {
ams::socket::SetLastError(ams::socket::Errno::ENoSpc);
rc = -1;
return rc;
}
for (T *addr_info = std::addressof(out); addr_info != nullptr; addr_info = addr_info->ai_next) {
if ((rc = FromBufferInternalImpl(*addr_info, cur, src_size - (cur - src))) == -1) {
rc = -1;
return rc;
}
cur += rc;
}
rc = cur - src;
return rc;
}
}
template<> size_t DNSSerializer::SizeOf(const struct addrinfo &in) {
return SizeOfImpl(in);
}
template<> size_t DNSSerializer::SizeOf(const ams::socket::AddrInfo &in) {
return SizeOfImpl(in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::AddrInfo &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct addrinfo &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::FromBuffer(ams::socket::AddrInfo &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
template<> ssize_t DNSSerializer::FromBuffer(struct addrinfo &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
void FreeAddrInfo(ams::socket::AddrInfo &addr_info) {
return FreeAddrInfoImpl(addr_info);
}
void FreeAddrInfo(struct addrinfo &addr_info) {
return FreeAddrInfoImpl(addr_info);
}
}

View File

@@ -1,234 +0,0 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
namespace {
template<typename T>
concept IsHostEnt = std::same_as<T, ams::socket::HostEnt> || std::same_as<T, struct hostent>;
template<typename T> requires IsHostEnt<T>
using InAddrType = typename std::conditional<std::same_as<T, ams::socket::HostEnt>, ams::socket::InAddr, struct in_addr>::type;
template<typename T> requires IsHostEnt<T>
constexpr bool IsAfInet(const auto h_addrtype) {
if constexpr (std::same_as<T, ams::socket::HostEnt>) {
return h_addrtype == ams::socket::Family::Af_Inet;
} else {
return h_addrtype == AF_INET;
}
}
template<typename T> requires IsHostEnt<T>
constexpr bool IsAfInet6(const auto h_addrtype) {
if constexpr (std::same_as<T, ams::socket::HostEnt>) {
return h_addrtype == ams::socket::Family::Af_Inet6;
} else {
return h_addrtype == AF_INET;
}
}
template<typename T> requires IsHostEnt<T>
size_t SizeOfImpl(const T &in) {
size_t rc = 0;
u32 dummy = 0;
rc += DNSSerializer::SizeOf((const char *)(in.h_name));
rc += DNSSerializer::SizeOf((const char **)(in.h_aliases), dummy);
rc += sizeof(u32);
rc += sizeof(u32);
rc += DNSSerializer::SizeOf((const InAddrType<T> **)(in.h_addr_list), dummy);
return rc;
}
template<typename T> requires IsHostEnt<T>
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
ssize_t rc = -1;
u8 *cur = dst;
const size_t required = DNSSerializer::SizeOf(in);
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, required, __LINE__)) == -1) {
return rc;
}
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), in.h_name)) == -1) {
return rc;
}
cur += rc;
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), in.h_aliases)) == -1) {
return rc;
}
cur += rc;
const u16 h_addrtype = static_cast<u16>(in.h_addrtype);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), h_addrtype)) == -1) {
return rc;
}
cur += rc;
const u16 h_length = in.h_length;
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), h_length)) == -1) {
return rc;
}
cur += rc;
if (IsAfInet<T>(in.h_addrtype)) {
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), (InAddrType<T> **)(in.h_addr_list))) == -1) {
return rc;
}
} else if (IsAfInet6<T>(in.h_addrtype)) {
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), (InAddrType<T> **)(in.h_addr_list))) == -1) {
return rc;
}
} else {
const u32 null = 0;
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), null)) == -1) {
return rc;
}
}
cur += rc;
rc = cur - dst;
return rc;
}
template<typename T> requires IsHostEnt<T>
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
ssize_t rc = -1;
const u8 *cur = src;
std::memset(std::addressof(out), 0, sizeof(out));
ON_SCOPE_EXIT {
if (rc < 0) {
FreeHostent(out);
}
};
if ((rc = DNSSerializer::FromBuffer(out.h_name, cur, src_size - (cur - src))) == -1) {
return rc;
}
cur += rc;
if ((rc = DNSSerializer::FromBuffer(out.h_aliases, cur, src_size - (cur - src))) == -1) {
return rc;
}
cur += rc;
u16 h_addrtype = 0;
if ((rc = DNSSerializer::FromBuffer(h_addrtype, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.h_addrtype = static_cast<decltype(out.h_addrtype)>(h_addrtype);
cur += rc;
u16 h_length = 0;
if ((rc = DNSSerializer::FromBuffer(h_length, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.h_length = h_length;
cur += rc;
InAddrType<T> **addrs = nullptr;
if ((rc = DNSSerializer::FromBuffer(addrs, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.h_addr_list = (char **)addrs;
cur += rc;
rc = cur - src;
return rc;
}
template<typename T> requires IsHostEnt<T>
void FreeHostentImpl(T &ent) {
if (ent.h_name != nullptr) {
ams::socket::impl::Free(ent.h_name);
ent.h_name = nullptr;
}
if (ent.h_aliases != nullptr) {
u32 i = 0;
for (char *str = ent.h_aliases[i]; str != nullptr; str = ent.h_aliases[++i]) {
ams::socket::impl::Free(str);
ent.h_aliases[i] = nullptr;
}
ams::socket::impl::Free(ent.h_aliases);
ent.h_aliases = nullptr;
}
if (ent.h_addr_list != nullptr) {
AMS_ASSERT(ent.h_length == sizeof(u32));
if (ent.h_length == sizeof(u32)) {
auto **addr_list = reinterpret_cast<InAddrType<T> **>(ent.h_addr_list);
u32 i = 0;
for (auto *addr = addr_list[i]; addr != nullptr; addr = addr_list[++i]) {
ams::socket::impl::Free(addr);
addr_list[i] = nullptr;
}
ams::socket::impl::Free(ent.h_addr_list);
ent.h_addr_list = nullptr;
}
}
std::memset(std::addressof(ent), 0, sizeof(ent));
}
}
template<> size_t DNSSerializer::SizeOf(const struct hostent &in) {
return SizeOfImpl(in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct hostent &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::FromBuffer(struct hostent &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
template<> size_t DNSSerializer::SizeOf(const ams::socket::HostEnt &in) {
return SizeOfImpl(in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::HostEnt &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::FromBuffer(ams::socket::HostEnt &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
void FreeHostent(ams::socket::HostEnt &ent) {
return FreeHostentImpl(ent);
}
void FreeHostent(struct hostent &ent) {
return FreeHostentImpl(ent);
}
}

View File

@@ -1,237 +0,0 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
namespace {
template<typename T>
concept IsInAddr = std::same_as<T, ams::socket::InAddr> || std::same_as<T, struct in_addr>;
template<typename T> requires IsInAddr<T>
size_t SizeOfImpl(const T &) {
return sizeof(u32);
}
template<typename T> requires IsInAddr<T>
size_t SizeOfImpl(const T **in, u32 &out_count) {
size_t rc = sizeof(u32);
out_count = 0;
if (in != nullptr) {
for (const T ** cur_addr = in; *cur_addr != nullptr; ++cur_addr) {
++out_count;
rc += sizeof(u32);
}
}
return rc;
}
template<typename T> requires IsInAddr<T>
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
ssize_t rc = -1;
u8 *cur = dst;
const u32 val = DNSSerializer::InternalHton(in.s_addr);
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, sizeof(in), __LINE__)) == -1) {
return rc;
}
std::memcpy(cur, std::addressof(val), sizeof(val));
rc += sizeof(val);
return rc;
}
template<typename T> requires IsInAddr<T>
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
ssize_t rc = -1;
if (src_size < sizeof(out)) {
return rc;
}
std::memset(std::addressof(out), 0, sizeof(out));
out.s_addr = DNSSerializer::InternalNtoh(*reinterpret_cast<const u32 *>(src));
rc = sizeof(u32);
return rc;
}
template<typename T> requires IsInAddr<T>
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, T **arr) {
ssize_t rc = -1;
u8 *cur = dst;
if (arr == nullptr && dst_size < sizeof(u32)) {
return rc;
} else if (arr == nullptr) {
const u32 null = 0;
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), null)) == -1) {
return rc;
}
cur += rc;
} else {
u32 count = 0;
for (auto *tmp = arr; *tmp != nullptr; ++tmp) {
++count;
}
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, (count + 1) * sizeof(**arr), __LINE__)) == -1) {
return rc;
}
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), count)) == -1) {
return rc;
}
cur += rc;
rc = 0;
for (auto i = 0; arr[i] != nullptr; ++i) {
const T addr = *arr[i];
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), addr)) == -1) {
return rc;
}
cur += rc;
}
rc = cur - dst;
}
return rc;
}
template<typename T> requires IsInAddr<T>
ssize_t FromBufferImpl(T **&out, const u8 *src, size_t src_size) {
ssize_t rc = -1;
const u8 *cur = src;
out = nullptr;
ON_SCOPE_EXIT {
if (rc == -1 && out != nullptr) {
for (auto i = 0; out[i] != nullptr; ++i) {
ams::socket::impl::Free(out[i]);
out[i] = nullptr;
}
ams::socket::impl::Free(out);
out = nullptr;
}
};
if (src == nullptr) {
rc = 0;
return rc;
} else if (src_size == 0) {
rc = 0;
return rc;
}
u32 count = 0;
if ((rc = DNSSerializer::FromBuffer(count, cur, src_size)) == -1) {
return rc;
}
cur += rc;
if (count == 0) {
return rc;
}
out = static_cast<T **>(ams::socket::impl::Alloc((count + 1) * sizeof(T *)));
if (out == nullptr) {
rc = -1;
return rc;
}
std::memset(out, 0, (count + 1) * sizeof(T *));
for (u32 i = 0; i < count; ++i) {
out[i] = static_cast<T *>(ams::socket::impl::Alloc(sizeof(T)));
if (out[i] == nullptr) {
rc = -1;
return rc;
}
u32 s_addr = 0;
if ((rc = DNSSerializer::FromBuffer(s_addr, cur, src_size - (cur - src))) == -1) {
return rc;
}
out[i]->s_addr = s_addr;
cur += rc;
}
out[count] = nullptr;
rc = cur - src;
return rc;
}
}
template<> size_t DNSSerializer::SizeOf(const struct in_addr &in) {
return SizeOfImpl(in);
}
template<> size_t DNSSerializer::SizeOf(const ams::socket::InAddr &in) {
return SizeOfImpl(in);
}
template<> size_t DNSSerializer::SizeOf(const struct in_addr **in, u32 &out_count) {
return SizeOfImpl(in, out_count);
}
template<> size_t DNSSerializer::SizeOf(const ams::socket::InAddr **in, u32 &out_count) {
return SizeOfImpl(in, out_count);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct in_addr &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::InAddr &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::FromBuffer(struct in_addr &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
template<> ssize_t DNSSerializer::FromBuffer(struct ams::socket::InAddr &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, struct in_addr **arr) {
return ToBufferImpl(dst, dst_size, arr);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, ams::socket::InAddr **arr) {
return ToBufferImpl(dst, dst_size, arr);
}
template<> ssize_t DNSSerializer::FromBuffer(struct in_addr **&out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
template<> ssize_t DNSSerializer::FromBuffer(struct ams::socket::InAddr **&out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
}

View File

@@ -1,74 +0,0 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const u16 &in) {
/* Convert the value. */
u8 *cur = dst;
const u16 val = InternalHton(in);
/* Check arguments. */
ssize_t rc = -1;
if ((rc = CheckToBufferArguments(cur, dst_size, sizeof(u16), __LINE__)) == -1) {
return rc;
}
std::memcpy(cur, std::addressof(val), sizeof(u16));
rc += sizeof(u16);
return rc;
}
template<> ssize_t DNSSerializer::FromBuffer(u16 &out, const u8 *src, size_t src_size) {
if (src_size < sizeof(u16)) {
return -1;
}
out = InternalNtoh(*reinterpret_cast<const u16 *>(src));
return sizeof(u16);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const u32 &in) {
/* Convert the value. */
u8 *cur = dst;
const u32 val = InternalHton(in);
/* Check arguments. */
ssize_t rc = -1;
if ((rc = CheckToBufferArguments(cur, dst_size, sizeof(u32), __LINE__)) == -1) {
return rc;
}
std::memcpy(cur, std::addressof(val), sizeof(u32));
rc += sizeof(u32);
return rc;
}
template<> ssize_t DNSSerializer::FromBuffer(u32 &out, const u8 *src, size_t src_size) {
if (src_size < sizeof(u32)) {
return -1;
}
out = InternalNtoh(*reinterpret_cast<const u32 *>(src));
return sizeof(u32);
}
}

View File

@@ -1,141 +0,0 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
namespace {
template<typename T>
concept IsSockAddrIn = std::same_as<T, ams::socket::SockAddrIn> || std::same_as<T, struct sockaddr_in>;
template<typename T> requires IsSockAddrIn<T>
size_t SizeOfImpl(const T &in) {
size_t rc = 0;
rc += sizeof(u16);
rc += sizeof(u16);
rc += DNSSerializer::SizeOf(in.sin_addr);
rc += sizeof(in.sin_zero);
return rc;
}
template<typename T> requires IsSockAddrIn<T>
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
ssize_t rc = -1;
u8 *cur = dst;
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, sizeof(in), __LINE__)) == -1) {
return rc;
}
const u16 sin_family = static_cast<u16>(in.sin_family);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin_family)) == -1) {
return rc;
}
cur += rc;
const u16 sin_port = static_cast<u16>(in.sin_port);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin_port)) == -1) {
return rc;
}
cur += rc;
const u32 s_addr = static_cast<u32>(in.sin_addr.s_addr);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), s_addr)) == -1) {
return rc;
}
cur += rc;
if (dst_size - (cur - dst) < sizeof(in.sin_zero)) {
rc = -1;
return rc;
}
std::memcpy(cur, in.sin_zero, sizeof(in.sin_zero));
cur += sizeof(in.sin_zero);
rc = cur - dst;
return rc;
}
template<typename T> requires IsSockAddrIn<T>
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
ssize_t rc = -1;
const u8 *cur = src;
u16 sin_family;
if ((rc = DNSSerializer::FromBuffer(sin_family, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.sin_family = static_cast<decltype(out.sin_family)>(sin_family);
cur += rc;
u16 sin_port;
if ((rc = DNSSerializer::FromBuffer(sin_port, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.sin_port = static_cast<decltype(out.sin_port)>(sin_port);
cur += rc;
u32 s_addr;
if ((rc = DNSSerializer::FromBuffer(s_addr, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.sin_addr.s_addr = static_cast<decltype(out.sin_addr.s_addr)>(s_addr);
cur += rc;
if (src_size - (cur - src) < sizeof(out.sin_zero)) {
rc = -1;
return rc;
}
std::memcpy(out.sin_zero, cur, sizeof(out.sin_zero));
cur += sizeof(out.sin_zero);
rc = cur - src;
return rc;
}
}
template<> size_t DNSSerializer::SizeOf(const struct sockaddr_in &in) {
return SizeOfImpl(in);
}
template<> size_t DNSSerializer::SizeOf(const ams::socket::SockAddrIn &in) {
return SizeOfImpl(in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct sockaddr_in &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::SockAddrIn &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::FromBuffer(struct sockaddr_in &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
template<> ssize_t DNSSerializer::FromBuffer(struct ams::socket::SockAddrIn &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
}

View File

@@ -1,133 +0,0 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
namespace {
template<typename T>
concept IsSockAddrIn6 = std::same_as<T, struct sockaddr_in6>;
template<typename T> requires IsSockAddrIn6<T>
size_t SizeOfImpl(const T &in) {
size_t rc = 0;
rc += sizeof(u16);
rc += sizeof(u16);
rc += sizeof(u32);
rc += DNSSerializer::SizeOf(in.sin6_addr);
rc += sizeof(u32);
return rc;
}
template<typename T> requires IsSockAddrIn6<T>
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
ssize_t rc = -1;
u8 *cur = dst;
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, sizeof(in), __LINE__)) == -1) {
return rc;
}
const u16 sin6_family = static_cast<u16>(in.sin6_family);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_family)) == -1) {
return rc;
}
cur += rc;
const u16 sin6_port = static_cast<u16>(in.sin6_port);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_port)) == -1) {
return rc;
}
cur += rc;
const u32 sin6_flowinfo = static_cast<u32>(in.sin6_flowinfo);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_flowinfo)) == -1) {
return rc;
}
cur += rc;
std::memcpy(cur, std::addressof(in.sin6_addr), sizeof(in.sin6_addr));
cur += sizeof(in.sin6_addr);
const u32 sin6_scope_id = static_cast<u32>(in.sin6_scope_id);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_scope_id)) == -1) {
return rc;
}
cur += rc;
rc = cur - dst;
return rc;
}
template<typename T> requires IsSockAddrIn6<T>
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
ssize_t rc = -1;
const u8 *cur = src;
u16 sin6_family;
if ((rc = DNSSerializer::FromBuffer(sin6_family, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.sin6_family = static_cast<decltype(out.sin6_family)>(sin6_family);
cur += rc;
u16 sin6_port;
if ((rc = DNSSerializer::FromBuffer(sin6_port, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.sin6_port = static_cast<decltype(out.sin6_port)>(sin6_port);
cur += rc;
u32 sin6_flowinfo;
if ((rc = DNSSerializer::FromBuffer(sin6_flowinfo, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.sin6_flowinfo = static_cast<decltype(out.sin6_flowinfo)>(sin6_flowinfo);
cur += rc;
std::memcpy(std::addressof(out.sin6_addr), cur, sizeof(out.sin6_addr));
cur += sizeof(out.sin6_addr);
u32 sin6_scope_id;
if ((rc = DNSSerializer::FromBuffer(sin6_scope_id, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.sin6_scope_id = static_cast<decltype(out.sin6_scope_id)>(sin6_scope_id);
cur += rc;
rc = cur - src;
return rc;
}
}
template<> size_t DNSSerializer::SizeOf(const struct sockaddr_in6 &in) {
return SizeOfImpl(in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct sockaddr_in6 &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::FromBuffer(struct sockaddr_in6 &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
}

View File

@@ -1,180 +0,0 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
template<> size_t DNSSerializer::SizeOf(const char *str) {
if (str == nullptr) {
return sizeof(char);
}
return std::strlen(str) + 1;
}
template<> size_t DNSSerializer::SizeOf(const char **str, u32 &out_count) {
size_t rc = sizeof(u32);
out_count = 0;
if (str != nullptr) {
for (const char **cur = str; *cur != nullptr; ++cur) {
++out_count;
rc += SizeOf(*cur);
}
}
return rc;
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, char *str) {
ssize_t rc = -1;
u8 *cur = dst;
if (str == nullptr && dst_size == 0) {
return -1;
} else if (str == nullptr) {
*cur = '\x00';
return 1;
} else if ((rc = SizeOf(static_cast<const char *>(str))) == 0) {
*cur = '\x00';
return 1;
} else if (CheckToBufferArguments(cur, dst_size, rc + 1, __LINE__) == -1) {
return -1;
}
std::memmove(cur, str, rc);
return rc;
}
template<> ssize_t DNSSerializer::FromBuffer(char *&out, const u8 *src, size_t src_size) {
size_t len = 0;
if (src == nullptr) {
return 0;
} else if (src_size == 0) {
return 0;
} else if (src_size < (len = SizeOf(reinterpret_cast<const char *>(src)))) {
return 1;
} else if (src[0] == '\x00') {
return 1;
}
out = static_cast<char *>(ams::socket::impl::Alloc(len));
if (out == nullptr) {
return -1;
}
std::memmove(out, src, len);
return len;
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, char **str) {
ssize_t rc = -1;
u8 *cur = dst;
u32 count = 0;
if (dst_size == 0) {
return -1;
}
const size_t total_size = SizeOf(const_cast<const char **>(str), count);
AMS_UNUSED(total_size);
if ((rc = CheckToBufferArguments(cur, dst_size, sizeof(u32), __LINE__)) == -1) {
return rc;
} else if ((rc = ToBuffer(cur, dst_size, count)) == -1) {
return rc;
}
cur += rc;
dst_size -= rc;
if (str != nullptr) {
for (char **cur_str = str; *cur_str != nullptr; ++cur_str) {
const auto tmp = ToBuffer(cur, dst_size, *cur_str);
if (tmp == -1) {
return rc;
}
cur += tmp;
dst_size -= tmp;
rc += tmp;
}
}
rc = cur - dst;
return rc;
}
template<> ssize_t DNSSerializer::FromBuffer(char **&out, const u8 *src, size_t src_size) {
ssize_t rc = -1;
const u8 *cur = src;
u32 count = 0;
out = nullptr;
ON_SCOPE_EXIT {
if (rc < 0 && out != nullptr) {
u32 i = 0;
for (char *str = *out; str != nullptr; str = out[++i]) {
ams::socket::impl::Free(str);
}
ams::socket::impl::Free(out);
out = nullptr;
}
};
if (src == nullptr) {
rc = 0;
return rc;
} else if (src_size == 0) {
rc = 0;
return rc;
} else if ((rc = FromBuffer(count, cur, src_size)) == -1) {
rc = -1;
return rc;
}
cur += rc;
out = static_cast<char **>(ams::socket::impl::Alloc((count + 1) * sizeof(char *)));
if (out == nullptr) {
rc = -1;
return rc;
}
std::memset(out, 0, (count + 1) * sizeof(char *));
u32 i;
for (i = 0; i < count; ++i) {
const size_t len = std::strlen(reinterpret_cast<const char *>(cur));
out[i] = static_cast<char *>(ams::socket::impl::Alloc(len + 1));
if (out[i] == nullptr) {
rc = -1;
return rc;
}
std::memmove(out[i], cur, len + 1);
cur += len + 1;
}
out[i] = nullptr;
rc = cur - src;
return rc;
}
}

View File

@@ -1,97 +0,0 @@
/*
* 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 "sfdnsres_shim.h"
#include <stratosphere/sf/sf_mitm_dispatch.h>
/* Command forwarders. */
Result sfdnsresGetHostByNameRequestWithOptionsFwd(Service *s, u64 process_id, const void *name, size_t name_size, void *out_hostent, size_t out_hostent_size, u32 *out_size, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno) {
const struct {
u32 options_version;
u32 num_options;
u64 process_id;
} in = { options_version, num_options, process_id };
struct {
u32 size;
s32 host_error;
s32 errno;
} out;
Result rc = serviceMitmDispatchInOut(s, 10, in, out,
.buffer_attrs = {
SfBufferAttr_HipcAutoSelect | SfBufferAttr_In,
SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out,
SfBufferAttr_HipcAutoSelect | SfBufferAttr_In
},
.buffers = {
{ name, name_size },
{ out_hostent, out_hostent_size },
{ option, option_size }
},
.in_send_pid = true,
.override_pid = process_id,
);
if (R_SUCCEEDED(rc)) {
if (out_size) *out_size = out.size;
if (out_host_error) *out_host_error = out.host_error;
if (out_errno) *out_errno = out.errno;
}
return rc;
}
Result sfdnsresGetAddrInfoRequestWithOptionsFwd(Service *s, u64 process_id, const void *node, size_t node_size, const void *srv, size_t srv_size, const void *hint, size_t hint_size, void *out_ai, size_t out_ai_size, u32 *out_size, s32 *out_rv, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno) {
const struct {
u32 options_version;
u32 num_options;
u64 process_id;
} in = { options_version, num_options, process_id };
struct {
u32 size;
s32 rv;
s32 host_error;
s32 errno;
} out;
Result rc = serviceMitmDispatchInOut(s, 12, in, out,
.buffer_attrs = {
SfBufferAttr_HipcMapAlias | SfBufferAttr_In,
SfBufferAttr_HipcMapAlias | SfBufferAttr_In,
SfBufferAttr_HipcMapAlias | SfBufferAttr_In,
SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out,
SfBufferAttr_HipcAutoSelect | SfBufferAttr_In
},
.buffers = {
{ node, node_size },
{ srv, srv_size },
{ hint, hint_size },
{ out_ai, out_ai_size },
{ option, option_size }
},
.in_send_pid = true,
.override_pid = process_id,
);
if (R_SUCCEEDED(rc)) {
if (out_size) *out_size = out.size;
if (out_rv) *out_rv = out.rv;
if (out_host_error) *out_host_error = out.host_error;
if (out_errno) *out_errno = out.errno;
}
return rc;
}

View File

@@ -1,21 +0,0 @@
/**
* @file sfdnsres_shim.h
* @brief IPC wrapper for dns.mitm.
* @author SciresM
* @copyright libnx Authors
*/
#pragma once
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Command forwarders. */
Result sfdnsresGetHostByNameRequestWithOptionsFwd(Service *s, u64 process_id, const void *name, size_t name_size, void *out_hostent, size_t out_hostent_size, u32 *out_size, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno);
Result sfdnsresGetAddrInfoRequestWithOptionsFwd(Service *s, u64 process_id, const void *node, size_t node_size, const void *srv, size_t srv_size, const void *hint, size_t hint_size, void *out_ai, size_t out_ai_size, u32 *out_size, s32 *out_rv, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno);
#ifdef __cplusplus
}
#endif

View File

@@ -1,25 +0,0 @@
/*
* 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::socket::impl {
void *Alloc(size_t size);
void *Calloc(size_t num, size_t size);
void Free(void *ptr);
}

View File

@@ -1,383 +0,0 @@
/*
* 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 "../amsmitm_fs_utils.hpp"
#include "../amsmitm_initialization.hpp"
#include "fs_shim.h"
#include "fs_mitm_service.hpp"
#include "fsmitm_boot0storage.hpp"
#include "fsmitm_calibration_binary_storage.hpp"
#include "fsmitm_layered_romfs_storage.hpp"
#include "fsmitm_save_utils.hpp"
#include "fsmitm_readonly_layered_filesystem.hpp"
namespace ams::mitm::fs {
using namespace ams::fs;
namespace {
constexpr const ams::fs::Path AtmosphereHblWebContentDirPath = fs::MakeConstantPath("/atmosphere/hbl_html/");
constexpr const char ProgramWebContentDir[] = "/manual_html/";
constinit os::SdkMutex g_boot0_detect_lock;
constinit bool g_detected_boot0_kind = false;
constinit bool g_is_boot0_custom_public_key = false;
constinit fssrv::impl::ProgramIndexMapInfoManager g_program_index_map_info_manager;
bool IsBoot0CustomPublicKey(::FsStorage &storage) {
if (AMS_UNLIKELY(!g_detected_boot0_kind)) {
std::scoped_lock lk(g_boot0_detect_lock);
if (AMS_LIKELY(!g_detected_boot0_kind)) {
g_is_boot0_custom_public_key = DetectBoot0CustomPublicKey(storage);
g_detected_boot0_kind = true;
}
}
return g_is_boot0_custom_public_key;
}
bool GetSettingsItemBooleanValue(const char *name, const char *key) {
u8 tmp = 0;
AMS_ABORT_UNLESS(settings::fwdbg::GetSettingsItemValue(std::addressof(tmp), sizeof(tmp), name, key) == sizeof(tmp));
return (tmp != 0);
}
template<typename... Arguments>
constexpr ALWAYS_INLINE auto MakeSharedFileSystem(Arguments &&... args) {
return sf::CreateSharedObjectEmplaced<ams::fssrv::sf::IFileSystem, ams::fssrv::impl::FileSystemInterfaceAdapter>(std::forward<Arguments>(args)...);
}
template<typename... Arguments>
constexpr ALWAYS_INLINE auto MakeSharedStorage(Arguments &&... args) {
return sf::CreateSharedObjectEmplaced<ams::fssrv::sf::IStorage, ams::fssrv::impl::StorageInterfaceAdapter>(std::forward<Arguments>(args)...);
}
Result OpenHblWebContentFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> &out, ncm::ProgramId program_id) {
/* Verify eligibility. */
bool is_hbl;
R_UNLESS(R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), program_id)), sm::mitm::ResultShouldForwardToSession());
R_UNLESS(is_hbl, sm::mitm::ResultShouldForwardToSession());
/* Hbl html directory must exist. */
{
FsDir d;
R_UNLESS(R_SUCCEEDED(mitm::fs::OpenSdDirectory(std::addressof(d), AtmosphereHblWebContentDirPath.GetString(), fs::OpenDirectoryMode_Directory)), sm::mitm::ResultShouldForwardToSession());
fsDirClose(std::addressof(d));
}
/* Open the SD card using fs.mitm's session. */
FsFileSystem sd_fs;
R_TRY(fsOpenSdCardFileSystem(std::addressof(sd_fs)));
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(sd_fs.s))};
std::unique_ptr<fs::fsa::IFileSystem> sd_ifs = std::make_unique<fs::RemoteFileSystem>(sd_fs);
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(std::move(sd_ifs));
R_TRY(subdir_fs->Initialize(AtmosphereHblWebContentDirPath));
out.SetValue(MakeSharedFileSystem(std::make_shared<fs::ReadOnlyFileSystem>(std::move(subdir_fs)), false), target_object_id);
R_SUCCEED();
}
Result OpenProgramSpecificWebContentFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> &out, ncm::ProgramId program_id, FsFileSystemType filesystem_type, Service *fwd, const fssrv::sf::Path *path, bool with_id) {
/* Directory must exist. */
R_UNLESS(HasSdManualHtmlContent(program_id), sm::mitm::ResultShouldForwardToSession());
/* Open the SD card using fs.mitm's session. */
FsFileSystem sd_fs;
R_TRY(fsOpenSdCardFileSystem(std::addressof(sd_fs)));
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(sd_fs.s))};
std::unique_ptr<fs::fsa::IFileSystem> sd_ifs = std::make_unique<fs::RemoteFileSystem>(sd_fs);
/* Format the subdirectory path. */
char program_web_content_raw_path[0x100];
FormatAtmosphereSdPath(program_web_content_raw_path, sizeof(program_web_content_raw_path), program_id, ProgramWebContentDir);
ams::fs::Path program_web_content_path;
R_TRY(program_web_content_path.SetShallowBuffer(program_web_content_raw_path));
/* Make a new filesystem. */
{
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(std::move(sd_ifs));
R_TRY(subdir_fs->Initialize(program_web_content_path));
std::shared_ptr<fs::fsa::IFileSystem> new_fs = nullptr;
/* Try to open the existing fs. */
FsFileSystem base_fs;
bool opened_base_fs = false;
if (with_id) {
opened_base_fs = R_SUCCEEDED(fsOpenFileSystemWithIdFwd(fwd, std::addressof(base_fs), static_cast<u64>(program_id), filesystem_type, path->str));
} else {
opened_base_fs = R_SUCCEEDED(fsOpenFileSystemWithPatchFwd(fwd, std::addressof(base_fs), static_cast<u64>(program_id), filesystem_type));
}
if (opened_base_fs) {
/* Create a layered adapter. */
new_fs = std::make_shared<ReadOnlyLayeredFileSystem>(std::move(subdir_fs), std::make_unique<fs::RemoteFileSystem>(base_fs));
} else {
/* Without an existing FS, just make a read only adapter to the subdirectory. */
new_fs = std::make_shared<fs::ReadOnlyFileSystem>(std::move(subdir_fs));
}
out.SetValue(MakeSharedFileSystem(std::move(new_fs), false), target_object_id);
}
R_SUCCEED();
}
Result OpenWebContentFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> &out, ncm::ProgramId client_program_id, ncm::ProgramId program_id, FsFileSystemType filesystem_type, Service *fwd, const fssrv::sf::Path *path, bool with_id, bool try_program_specific) {
/* Check first that we're a web applet opening web content. */
R_UNLESS(ncm::IsWebAppletId(client_program_id), sm::mitm::ResultShouldForwardToSession());
R_UNLESS(filesystem_type == FsFileSystemType_ContentManual, sm::mitm::ResultShouldForwardToSession());
/* Try to mount the HBL web filesystem. If this succeeds then we're done. */
R_SUCCEED_IF(R_SUCCEEDED(OpenHblWebContentFileSystem(out, program_id)));
/* If program specific override shouldn't be attempted, fall back. */
R_UNLESS(try_program_specific, sm::mitm::ResultShouldForwardToSession());
/* If we're not opening a HBL filesystem, just try to open a generic one. */
R_RETURN(OpenProgramSpecificWebContentFileSystem(out, program_id, filesystem_type, fwd, path, with_id));
}
}
bool HasSdManualHtmlContent(ncm::ProgramId program_id) {
/* Directory must exist. */
FsDir d;
if (R_SUCCEEDED(OpenAtmosphereSdDirectory(std::addressof(d), program_id, ProgramWebContentDir, fs::OpenDirectoryMode_Directory))) {
::fsDirClose(std::addressof(d));
return true;
} else {
return false;
}
}
Result FsMitmService::OpenFileSystemWithPatch(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id, u32 _filesystem_type) {
R_RETURN(OpenWebContentFileSystem(out, m_client_info.program_id, program_id, static_cast<FsFileSystemType>(_filesystem_type), m_forward_service.get(), nullptr, false, m_client_info.override_status.IsProgramSpecific()));
}
Result FsMitmService::OpenFileSystemWithId(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, const fssrv::sf::Path &path, ncm::ProgramId program_id, u32 _filesystem_type) {
R_RETURN(OpenWebContentFileSystem(out, m_client_info.program_id, program_id, static_cast<FsFileSystemType>(_filesystem_type), m_forward_service.get(), std::addressof(path), true, m_client_info.override_status.IsProgramSpecific()));
}
Result FsMitmService::OpenSdCardFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out) {
/* We only care about redirecting this for NS/emummc. */
R_UNLESS(m_client_info.program_id == ncm::SystemProgramId::Ns, sm::mitm::ResultShouldForwardToSession());
R_UNLESS(emummc::IsActive(), sm::mitm::ResultShouldForwardToSession());
/* Create a new SD card filesystem. */
FsFileSystem sd_fs;
R_TRY(fsOpenSdCardFileSystemFwd(m_forward_service.get(), std::addressof(sd_fs)));
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(sd_fs.s))};
/* Return output filesystem. */
auto redir_fs = std::make_shared<fssystem::DirectoryRedirectionFileSystem>(std::make_unique<RemoteFileSystem>(sd_fs));
R_TRY(redir_fs->InitializeWithFixedPath("/Nintendo", emummc::GetNintendoDirPath()));
out.SetValue(MakeSharedFileSystem(std::move(redir_fs), false), target_object_id);
R_SUCCEED();
}
Result FsMitmService::OpenSaveDataFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, u8 _space_id, const fs::SaveDataAttribute &attribute) {
/* We only want to intercept saves for games, right now. */
const bool is_game_or_hbl = m_client_info.override_status.IsHbl() || ncm::IsApplicationId(m_client_info.program_id);
R_UNLESS(is_game_or_hbl, sm::mitm::ResultShouldForwardToSession());
/* Only redirect if the appropriate system setting is set. */
R_UNLESS(GetSettingsItemBooleanValue("atmosphere", "fsmitm_redirect_saves_to_sd"), sm::mitm::ResultShouldForwardToSession());
/* Only redirect if the specific title being accessed has a redirect save flag. */
R_UNLESS(cfg::HasContentSpecificFlag(m_client_info.program_id, "redirect_save"), sm::mitm::ResultShouldForwardToSession());
/* Only redirect account savedata. */
R_UNLESS(attribute.type == fs::SaveDataType::Account, sm::mitm::ResultShouldForwardToSession());
/* Get enum type for space id. */
auto space_id = static_cast<FsSaveDataSpaceId>(_space_id);
/* Verify we can open the save. */
static_assert(sizeof(fs::SaveDataAttribute) == sizeof(::FsSaveDataAttribute));
FsFileSystem save_fs;
R_UNLESS(R_SUCCEEDED(fsOpenSaveDataFileSystemFwd(m_forward_service.get(), std::addressof(save_fs), space_id, reinterpret_cast<const FsSaveDataAttribute *>(std::addressof(attribute)))), sm::mitm::ResultShouldForwardToSession());
std::unique_ptr<fs::fsa::IFileSystem> save_ifs = std::make_unique<fs::RemoteFileSystem>(save_fs);
/* Mount the SD card using fs.mitm's session. */
FsFileSystem sd_fs;
R_TRY(fsOpenSdCardFileSystem(std::addressof(sd_fs)));
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(sd_fs.s))};
std::shared_ptr<fs::fsa::IFileSystem> sd_ifs = std::make_shared<fs::RemoteFileSystem>(sd_fs);
/* Verify that we can open the save directory, and that it exists. */
const ncm::ProgramId application_id = attribute.program_id == ncm::InvalidProgramId ? m_client_info.program_id : attribute.program_id;
char save_dir_raw_path[0x100];
R_TRY(mitm::fs::SaveUtil::GetDirectorySaveDataPath(save_dir_raw_path, sizeof(save_dir_raw_path), application_id, space_id, attribute));
ams::fs::Path save_dir_path;
R_TRY(save_dir_path.SetShallowBuffer(save_dir_raw_path));
/* Check if this is the first time we're making the save. */
bool is_new_save = false;
{
fs::DirectoryEntryType ent;
R_TRY_CATCH(sd_ifs->GetEntryType(std::addressof(ent), save_dir_path)) {
R_CATCH(fs::ResultPathNotFound) { is_new_save = true; }
R_CATCH_ALL() { /* ... */ }
} R_END_TRY_CATCH;
}
/* Ensure the directory exists. */
R_TRY(fssystem::EnsureDirectory(sd_ifs.get(), save_dir_path));
/* Create directory savedata filesystem. */
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(sd_ifs);
R_TRY(subdir_fs->Initialize(save_dir_path));
std::shared_ptr<fssystem::DirectorySaveDataFileSystem> dirsave_ifs = std::make_shared<fssystem::DirectorySaveDataFileSystem>(std::move(subdir_fs));
/* Ensure correct directory savedata filesystem state. */
R_TRY(dirsave_ifs->Initialize(true, true, true));
/* If it's the first time we're making the save, copy existing savedata over. */
if (is_new_save) {
/* TODO: Check error? */
fs::DirectoryEntry work_entry;
constexpr const fs::Path root_path = fs::MakeConstantPath("/");
u8 savedata_copy_buffer[2_KB];
fssystem::CopyDirectoryRecursively(dirsave_ifs.get(), save_ifs.get(), root_path, root_path, std::addressof(work_entry), savedata_copy_buffer, sizeof(savedata_copy_buffer));
}
/* Set output. */
out.SetValue(MakeSharedFileSystem(std::move(dirsave_ifs), false), target_object_id);
R_SUCCEED();
}
Result FsMitmService::OpenBisStorage(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u32 _bis_partition_id) {
const ::FsBisPartitionId bis_partition_id = static_cast<::FsBisPartitionId>(_bis_partition_id);
/* Try to open a storage for the partition. */
FsStorage bis_storage;
R_TRY(fsOpenBisStorageFwd(m_forward_service.get(), std::addressof(bis_storage), bis_partition_id));
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(bis_storage.s))};
const bool is_sysmodule = ncm::IsSystemProgramId(m_client_info.program_id);
const bool is_hbl = m_client_info.override_status.IsHbl();
const bool can_write_bis = is_sysmodule || (is_hbl && GetSettingsItemBooleanValue("atmosphere", "enable_hbl_bis_write"));
/* Allow HBL to write to boot1 (safe firm) + package2. */
/* This is needed to not break compatibility with ChoiDujourNX, which does not check for write access before beginning an update. */
/* TODO: get fixed so that this can be turned off without causing bricks :/ */
const bool is_package2 = (FsBisPartitionId_BootConfigAndPackage2Part1 <= bis_partition_id && bis_partition_id <= FsBisPartitionId_BootConfigAndPackage2Part6);
const bool is_boot1 = bis_partition_id == FsBisPartitionId_BootPartition2Root;
const bool can_write_bis_for_choi_support = is_hbl && (is_package2 || is_boot1);
/* Set output storage. */
if (bis_partition_id == FsBisPartitionId_BootPartition1Root) {
if (IsBoot0CustomPublicKey(bis_storage)) {
out.SetValue(MakeSharedStorage(std::make_shared<CustomPublicKeyBoot0Storage>(bis_storage, m_client_info, spl::GetSocType())), target_object_id);
} else {
out.SetValue(MakeSharedStorage(std::make_shared<Boot0Storage>(bis_storage, m_client_info)), target_object_id);
}
} else if (bis_partition_id == FsBisPartitionId_CalibrationBinary) {
out.SetValue(MakeSharedStorage(std::make_shared<CalibrationBinaryStorage>(bis_storage, m_client_info)), target_object_id);
} else {
if (can_write_bis || can_write_bis_for_choi_support) {
/* We can write, so create a writable storage. */
out.SetValue(MakeSharedStorage(std::make_shared<RemoteStorage>(bis_storage)), target_object_id);
} else {
/* We can only read, so create a readable storage. */
std::unique_ptr<ams::fs::IStorage> unique_bis = std::make_unique<RemoteStorage>(bis_storage);
out.SetValue(MakeSharedStorage(std::make_shared<ReadOnlyStorageAdapter>(std::move(unique_bis))), target_object_id);
}
}
R_SUCCEED();
}
Result FsMitmService::OpenDataStorageByCurrentProcess(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out) {
/* Only mitm if we should override contents for the current process. */
R_UNLESS(m_client_info.override_status.IsProgramSpecific(), sm::mitm::ResultShouldForwardToSession());
/* Only mitm if there is actually an override romfs. */
R_UNLESS(mitm::fs::HasSdRomfsContent(m_client_info.program_id), sm::mitm::ResultShouldForwardToSession());
/* Try to open the process romfs. */
FsStorage data_storage;
R_TRY(fsOpenDataStorageByCurrentProcessFwd(m_forward_service.get(), std::addressof(data_storage)));
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
/* Get a layered storage for the process romfs. */
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(m_client_info.program_id, data_storage, true)), target_object_id);
R_SUCCEED();
}
Result FsMitmService::OpenDataStorageByDataId(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, ncm::DataId _data_id, u8 storage_id) {
/* Only mitm if we should override contents for the current process. */
R_UNLESS(m_client_info.override_status.IsProgramSpecific(), sm::mitm::ResultShouldForwardToSession());
/* TODO: Decide how to handle DataId vs ProgramId for this API. */
const ncm::ProgramId data_id = {_data_id.value};
/* Only mitm if there is actually an override romfs. */
R_UNLESS(mitm::fs::HasSdRomfsContent(data_id), sm::mitm::ResultShouldForwardToSession());
/* Try to open the data id. */
FsStorage data_storage;
R_TRY(fsOpenDataStorageByDataIdFwd(m_forward_service.get(), std::addressof(data_storage), static_cast<u64>(data_id), static_cast<NcmStorageId>(storage_id)));
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
/* Get a layered storage for the data id. */
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(data_id, data_storage, false)), target_object_id);
R_SUCCEED();
}
Result FsMitmService::OpenDataStorageWithProgramIndex(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u8 program_index) {
/* Only mitm if we should override contents for the current process. */
R_UNLESS(m_client_info.override_status.IsProgramSpecific(), sm::mitm::ResultShouldForwardToSession());
/* Get the relevant program id. */
const ncm::ProgramId program_id = g_program_index_map_info_manager.GetProgramId(m_client_info.program_id, program_index);
/* If we don't know about the program or don't have content, forward. */
R_UNLESS(program_id != ncm::InvalidProgramId, sm::mitm::ResultShouldForwardToSession());
R_UNLESS(mitm::fs::HasSdRomfsContent(program_id), sm::mitm::ResultShouldForwardToSession());
/* Try to open the process romfs. */
FsStorage data_storage;
R_TRY(fsOpenDataStorageWithProgramIndexFwd(m_forward_service.get(), std::addressof(data_storage), program_index));
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
/* Get a layered storage for the process romfs. */
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(program_id, data_storage, true)), target_object_id);
R_SUCCEED();
}
Result FsMitmService::RegisterProgramIndexMapInfo(const sf::InBuffer &info_buffer, s32 info_count) {
/* Try to register with FS. */
R_TRY(fsRegisterProgramIndexMapInfoFwd(m_forward_service.get(), info_buffer.GetPointer(), info_buffer.GetSize(), info_count));
/* Register with ourselves. */
R_ABORT_UNLESS(g_program_index_map_info_manager.Reset(reinterpret_cast<const fs::ProgramIndexMapInfo *>(info_buffer.GetPointer()), info_count));
R_SUCCEED();
}
}

View File

@@ -1,92 +0,0 @@
/*
* 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>
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
#define AMS_FS_MITM_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 7, Result, OpenFileSystemWithPatch, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id, u32 _filesystem_type), (out, program_id, _filesystem_type), hos::Version_2_0_0) \
AMS_SF_METHOD_INFO(C, H, 8, Result, OpenFileSystemWithId, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, const fssrv::sf::Path &path, ncm::ProgramId program_id, u32 _filesystem_type), (out, path, program_id, _filesystem_type), hos::Version_2_0_0) \
AMS_SF_METHOD_INFO(C, H, 18, Result, OpenSdCardFileSystem, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 51, Result, OpenSaveDataFileSystem, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, u8 space_id, const ams::fs::SaveDataAttribute &attribute), (out, space_id, attribute)) \
AMS_SF_METHOD_INFO(C, H, 12, Result, OpenBisStorage, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u32 bis_partition_id), (out, bis_partition_id)) \
AMS_SF_METHOD_INFO(C, H, 200, Result, OpenDataStorageByCurrentProcess, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 202, Result, OpenDataStorageByDataId, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, ncm::DataId data_id, u8 storage_id), (out, data_id, storage_id)) \
AMS_SF_METHOD_INFO(C, H, 205, Result, OpenDataStorageWithProgramIndex, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u8 program_index), (out, program_index), hos::Version_7_0_0) \
AMS_SF_METHOD_INFO(C, H, 810, Result, RegisterProgramIndexMapInfo, (const sf::InBuffer &info_buffer, s32 info_count), (info_buffer, info_count), hos::Version_7_0_0)
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::fs, IFsMitmInterface, AMS_FS_MITM_INTERFACE_INFO, 0x7DF34ED2)
namespace ams::mitm::fs {
class FsMitmService : public sf::MitmServiceImplBase {
public:
using MitmServiceImplBase::MitmServiceImplBase;
public:
static constexpr ALWAYS_INLINE bool ShouldMitmProgramId(const ncm::ProgramId program_id) {
/* We want to mitm everything that isn't a system-module. */
if (!ncm::IsSystemProgramId(program_id)) {
return true;
}
/* We want to mitm ns, to intercept SD card requests and program index map info registration. */
if (program_id == ncm::SystemProgramId::Ns) {
return true;
}
/* We want to mitm settings, to intercept CAL0. */
if (program_id == ncm::SystemProgramId::Settings) {
return true;
}
/* We want to mitm sdb, to support sd-romfs redirection of common system archives (like system font, etc). */
/* NOTE: In 16.0.0+, this was moved to glue. */
if (program_id == ncm::SystemProgramId::Sdb || program_id == ncm::SystemProgramId::Glue) {
return true;
}
return false;
}
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
static std::atomic_bool has_launched_qlaunch = false;
/* TODO: intercepting everything seems to cause issues with sleep mode, for some reason. */
/* Figure out why, and address it. */
/* TODO: This may be because pre-rewrite code really mismanaged domain objects in a way that would cause bad things. */
/* Need to verify if this is fixed now. */
if (client_info.program_id == ncm::SystemAppletId::Qlaunch || client_info.program_id == ncm::SystemAppletId::MaintenanceMenu) {
has_launched_qlaunch = true;
}
return has_launched_qlaunch || ShouldMitmProgramId(client_info.program_id);
}
public:
/* Overridden commands. */
Result OpenFileSystemWithPatch(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id, u32 _filesystem_type);
Result OpenFileSystemWithId(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, const fssrv::sf::Path &path, ncm::ProgramId program_id, u32 _filesystem_type);
Result OpenSdCardFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out);
Result OpenSaveDataFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, u8 space_id, const ams::fs::SaveDataAttribute &attribute);
Result OpenBisStorage(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u32 bis_partition_id);
Result OpenDataStorageByCurrentProcess(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out);
Result OpenDataStorageByDataId(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, ncm::DataId data_id, u8 storage_id);
Result OpenDataStorageWithProgramIndex(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u8 program_index);
Result RegisterProgramIndexMapInfo(const sf::InBuffer &info_buffer, s32 info_count);
};
static_assert(IsIFsMitmInterface<FsMitmService>);
}

View File

@@ -1,105 +0,0 @@
/*
* 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 "fs_shim.h"
/* Missing fsp-srv commands. */
static Result _fsOpenSession(Service *s, Service* out, u32 cmd_id) {
return serviceDispatch(s, cmd_id,
.out_num_objects = 1,
.out_objects = out,
);
}
Result fsOpenSdCardFileSystemFwd(Service* s, FsFileSystem* out) {
return _fsOpenSession(s, &out->s, 18);
}
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisPartitionId partition_id) {
const u32 tmp = partition_id;
return serviceDispatchIn(s, 12, tmp,
.out_num_objects = 1,
.out_objects = &out->s,
);
}
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) {
return _fsOpenSession(s, &out->s, 200);
}
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorage* out, u64 data_id, NcmStorageId storage_id) {
const struct {
u8 storage_id;
u64 data_id;
} in = { storage_id, data_id };
return serviceDispatchIn(s, 202, in,
.out_num_objects = 1,
.out_objects = &out->s,
);
}
Result fsOpenDataStorageWithProgramIndexFwd(Service* s, FsStorage* out, u8 program_index) {
return serviceDispatchIn(s, 205, program_index,
.out_num_objects = 1,
.out_objects = &out->s,
);
}
Result fsRegisterProgramIndexMapInfoFwd(Service* s, const void *buf, size_t buf_size, s32 count) {
return serviceDispatchIn(s, 810, count,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
.buffers = { { buf, buf_size } },
);
}
Result fsOpenSaveDataFileSystemFwd(Service* s, FsFileSystem* out, FsSaveDataSpaceId save_data_space_id, const FsSaveDataAttribute *attr) {
const struct {
u8 save_data_space_id;
u8 pad[7];
FsSaveDataAttribute attr;
} in = { (u8)save_data_space_id, {0}, *attr };
return serviceDispatchIn(s, 51, in,
.out_num_objects = 1,
.out_objects = &out->s,
);
}
Result fsOpenFileSystemWithPatchFwd(Service* s, FsFileSystem* out, u64 id, FsFileSystemType fsType) {
const struct {
u32 fsType;
u64 id;
} in = { fsType, id };
return serviceDispatchIn(s, 7, in,
.out_num_objects = 1,
.out_objects = &out->s
);
}
Result fsOpenFileSystemWithIdFwd(Service* s, FsFileSystem* out, u64 id, FsFileSystemType fsType, const char* contentPath) {
const struct {
u32 fsType;
u64 id;
} in = { fsType, id };
return serviceDispatchIn(s, 8, in,
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
.buffers = { { contentPath, FS_MAX_PATH } },
.out_num_objects = 1,
.out_objects = &out->s
);
}

View File

@@ -1,31 +0,0 @@
/**
* @file fs_shim.h
* @brief Filesystem Services (fs) IPC wrapper for fs.mitm.
* @author SciresM
* @copyright libnx Authors
*/
#pragma once
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Missing fsp-srv commands. */
Result fsOpenSdCardFileSystemFwd(Service* s, FsFileSystem* out);
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisPartitionId partition_id);
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out);
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorage* out, u64 data_id, NcmStorageId storage_id);
Result fsOpenDataStorageWithProgramIndexFwd(Service* s, FsStorage* out, u8 program_index);
Result fsRegisterProgramIndexMapInfoFwd(Service* s, const void *buf, size_t buf_size, s32 count);
Result fsOpenSaveDataFileSystemFwd(Service* s, FsFileSystem* out, FsSaveDataSpaceId save_data_space_id, const FsSaveDataAttribute *attr);
Result fsOpenFileSystemWithPatchFwd(Service* s, FsFileSystem* out, u64 id, FsFileSystemType fsType);
Result fsOpenFileSystemWithIdFwd(Service* s, FsFileSystem* out, u64 id, FsFileSystemType fsType, const char* contentPath);
#ifdef __cplusplus
}
#endif

View File

@@ -1,267 +0,0 @@
/*
* 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 "fsmitm_boot0storage.hpp"
namespace ams::mitm::fs {
using namespace ams::fs;
namespace {
constinit os::SdkMutex g_boot0_access_mutex;
constinit bool g_custom_public_key = false;
constinit u8 g_boot0_bct_buffer[Boot0Storage::BctEndOffset];
/* Recognize special public key (https://gist.github.com/SciresM/16b63ac1d80494522bdba2c57995257c). */
/* P = 19 */
/* Q = 1696986749729493925354392349339746171297507422986462747526968361144447230710192316397327889522451749459854070558277878297255552508603806832852079596337539247651161831569525505882103311631577368514276343192042634740927726070847704397913856975832811679847928433261678072951551065705680482548543833651752439700272736498378724153330763357721354498194000536297732323628263256733931353143625854828275237159155585342783077681713929284136658773985266864804093157854331138230313706015557050002740810464618031715670281442110238274404626065924786185264268216336867948322976979393032640085259926883014490947373494538254895109731 */
/* N = 0xFF696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696959 */
/* E = 0x10001 */
/* D = 6512128715229088976470211610075969347035078304643231077895577077900787352712063823560162578441773733649014439616165727455431015055675770987914713980812453585413988983206576233689754710500864883529402371292948326392791238474661859182717295176679567362482790015587820446999760239570255254879359445627372805817473978644067558931078225451477635089763009580492462097185005355990612929951162366081631888011031830742459571000203341001926135389508196521518687349554188686396554248868403128728646457247197407637887195043486221295751496667162366700477934591694110831002874992896076061627516220934290742867397720103040314639313 */
constexpr const u8 CustomPublicKey[0x100] = {
0x59, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0xFF,
};
}
bool Boot0Storage::CanModifyBctPublicKey() {
if (exosphere::IsRcmBugPatched()) {
/* RCM bug patched. */
/* Only allow NS to update the BCT pubks. */
/* AutoRCM on a patched unit will cause a brick, so homebrew should NOT be allowed to write. */
return m_client_info.program_id == ncm::SystemProgramId::Ns;
} else {
/* RCM bug unpatched. */
/* Allow homebrew but not NS to update the BCT pubks. */
return m_client_info.override_status.IsHbl();
}
}
Result Boot0Storage::Read(s64 offset, void *_buffer, size_t size) {
std::scoped_lock lk{g_boot0_access_mutex};
AMS_ABORT_UNLESS(!g_custom_public_key);
/* Check if we have nothing to do. */
R_SUCCEED_IF(size == 0);
R_RETURN(Base::Read(offset, _buffer, size));
}
Result Boot0Storage::Write(s64 offset, const void *_buffer, size_t size) {
std::scoped_lock lk{g_boot0_access_mutex};
AMS_ABORT_UNLESS(!g_custom_public_key);
const u8 *buffer = static_cast<const u8 *>(_buffer);
/* Check if we have nothing to do. */
R_SUCCEED_IF(size == 0);
/* Protect the EKS region from writes. */
if (offset <= EksStart) {
if (offset + size < EksStart) {
/* Fall through, no need to do anything here. */
} else {
if (offset + size > EksEnd) {
/* Perform portion of write falling past end of keyblobs. */
const s64 diff = EksEnd - offset;
R_TRY(Base::Write(EksEnd, buffer + diff, size - diff));
}
/* Adjust size to avoid writing end of data. */
size = EksStart - offset;
}
} else if (offset < EksEnd) {
/* Ignore writes falling strictly within the region. */
R_SUCCEED_IF(offset + size <= EksEnd);
/* Only write past the end of the region. */
const s64 diff = EksEnd - offset;
buffer += diff;
size -= diff;
offset = EksEnd;
}
/* If we have nothing to write, succeed immediately. */
R_SUCCEED_IF(size == 0);
/* We want to protect AutoRCM from NS on ipatched units. If we can modify bct pubks or we're not touching any of them, proceed. */
if (this->CanModifyBctPublicKey() || offset >= BctEndOffset || (util::AlignUp(offset, BctSize) >= BctEndOffset && (offset % BctSize) >= BctPubkEnd)) {
R_RETURN(Base::Write(offset, buffer, size));
}
/* Handle any data written past the end of the pubk region. */
if (offset + size > BctEndOffset) {
const u64 diff = BctEndOffset - offset;
R_TRY(Base::Write(BctEndOffset, buffer + diff, size - diff));
size = diff;
}
/* Read in the current BCT region. */
R_TRY(Base::Read(0, g_boot0_bct_buffer, BctEndOffset));
/* Update the bct buffer. */
for (u64 cur_offset = offset; cur_offset < BctEndOffset && cur_offset < offset + size; cur_offset++) {
const u64 cur_bct_relative_ofs = cur_offset % BctSize;
if (cur_bct_relative_ofs < BctPubkStart || BctPubkEnd <= cur_bct_relative_ofs) {
g_boot0_bct_buffer[cur_offset] = buffer[cur_offset - offset];
}
}
R_RETURN(Base::Write(0, g_boot0_bct_buffer, BctEndOffset));
}
CustomPublicKeyBoot0Storage::CustomPublicKeyBoot0Storage(FsStorage &s, const sm::MitmProcessInfo &c, spl::SocType soc) : Base(s), m_client_info(c), m_soc_type(soc) {
std::scoped_lock lk{g_boot0_access_mutex};
/* We're custom public key. */
g_custom_public_key = true;
}
Result CustomPublicKeyBoot0Storage::Read(s64 offset, void *_buffer, size_t size) {
std::scoped_lock lk{g_boot0_access_mutex};
AMS_ABORT_UNLESS(g_custom_public_key);
/* Check if we have nothing to do. */
R_SUCCEED_IF(size == 0);
u8 *buffer = static_cast<u8 *>(_buffer);
/* Check if we're reading the first BCTs for NS. */
/* If we are, we want to lie about the contents of BCT0/1 so that they validate. */
if (offset < 0x8000 && m_client_info.program_id == ncm::SystemProgramId::Ns) {
R_TRY(Base::Read(0, g_boot0_bct_buffer, Boot0Storage::BctEndOffset));
/* Determine the readable size. */
const size_t readable_bct01_size = std::min<size_t>(0x8000, offset + size) - offset;
std::memcpy(buffer, g_boot0_bct_buffer + 0x8000 + offset, readable_bct01_size);
/* Advance. */
buffer += readable_bct01_size;
offset += readable_bct01_size;
size -= readable_bct01_size;
}
/* Check if we have nothing to do. */
R_SUCCEED_IF(size == 0);
/* Perform whatever remains of the read. */
R_RETURN(Base::Read(offset, buffer, size));
}
Result CustomPublicKeyBoot0Storage::Write(s64 offset, const void *_buffer, size_t size) {
std::scoped_lock lk{g_boot0_access_mutex};
AMS_ABORT_UNLESS(g_custom_public_key);
const u8 *buffer = static_cast<const u8 *>(_buffer);
/* Check if we have nothing to do. */
R_SUCCEED_IF(size == 0);
/* Drop writes to the first BCTs. */
if (offset < 0x8000) {
/* Determine the writable size. */
const size_t writable_bct01_size = std::min<size_t>(0x8000, offset + size) - offset;
/* Advance. */
buffer += writable_bct01_size;
offset += writable_bct01_size;
size -= writable_bct01_size;
}
/* Check if we have nothing to do. */
R_SUCCEED_IF(size == 0);
/* Similarly, we want to drop writes to the end of boot0, where custom bootloader lives. */
R_SUCCEED_IF(offset >= 0x380000);
if (offset + size >= 0x380000) {
size = 0x380000 - offset;
}
/* On erista, we want to protect the EKS region. */
if (m_soc_type == spl::SocType_Erista) {
if (offset <= Boot0Storage::EksStart) {
if (offset + size < Boot0Storage::EksStart) {
/* Fall through, no need to do anything here. */
} else {
if (offset + size > Boot0Storage::EksEnd) {
/* Perform portion of write falling past end of keyblobs. */
const s64 diff = Boot0Storage::EksEnd - offset;
R_TRY(Base::Write(Boot0Storage::EksEnd, buffer + diff, size - diff));
}
/* Adjust size to avoid writing end of data. */
size = Boot0Storage::EksStart - offset;
}
} else if (offset < Boot0Storage::EksEnd) {
/* Ignore writes falling strictly within the region. */
R_SUCCEED_IF(offset + size <= Boot0Storage::EksEnd);
/* Only write past the end of the region. */
const s64 diff = Boot0Storage::EksEnd - offset;
buffer += diff;
size -= diff;
offset = Boot0Storage::EksEnd;
}
}
/* If we have nothing to write, succeed immediately. */
R_SUCCEED_IF(size == 0);
/* Perform whatever remains of the write. */
R_RETURN(Base::Write(offset, buffer, size));
}
bool DetectBoot0CustomPublicKey(::FsStorage &storage) {
/* Determine public key offset. */
const size_t bct_pubk_offset = spl::GetSocType() == spl::SocType_Mariko ? 0x10 : 0x210;
u8 work_buffer[0x400];
/* Read BCT-Normal-Main. */
R_ABORT_UNLESS(::fsStorageRead(std::addressof(storage), 0, work_buffer, sizeof(work_buffer)));
/* Check for custom public key. */
if (std::memcmp(work_buffer + bct_pubk_offset, CustomPublicKey, sizeof(CustomPublicKey)) != 0) {
return false;
}
/* Read BCT-Safe-Main. */
R_ABORT_UNLESS(::fsStorageRead(std::addressof(storage), 0x4000, work_buffer, sizeof(work_buffer)));
/* Check for custom public key. */
if (std::memcmp(work_buffer + bct_pubk_offset, CustomPublicKey, sizeof(CustomPublicKey)) != 0) {
return false;
}
return true;
}
}

View File

@@ -1,164 +0,0 @@
/*
* 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::mitm::fs {
template<class Base, size_t SectorSize>
class SectoredStorageAdapter : public Base {
static_assert(std::is_base_of<ams::fs::IStorage, Base>::value);
private:
u8 m_sector_buf[SectorSize];
public:
/* Inherit constructors. */
using Base::Base;
public:
virtual Result Read(s64 offset, void *_buffer, size_t size) override {
u8 *buffer = static_cast<u8 *>(_buffer);
const s64 seek = util::AlignDown(offset, SectorSize);
const s64 sector_ofs = offset - seek;
/* Check if we have nothing to do. */
if (size == 0) {
R_SUCCEED();
}
/* Fast case. */
if (sector_ofs == 0 && util::IsAligned(size, SectorSize)) {
R_RETURN(Base::Read(offset, buffer, size));
}
R_TRY(Base::Read(seek, m_sector_buf, SectorSize));
if (size + sector_ofs <= SectorSize) {
/* Staying within the sector. */
std::memcpy(buffer, m_sector_buf + sector_ofs, size);
} else {
/* Leaving the sector. */
const size_t size_in_sector = SectorSize - sector_ofs;
std::memcpy(buffer, m_sector_buf + sector_ofs, size_in_sector);
size -= size_in_sector;
/* Read as many guaranteed aligned sectors as we can. */
const size_t aligned_remaining_size = util::AlignDown(size, SectorSize);
if (aligned_remaining_size) {
R_TRY(Base::Read(offset + size_in_sector, buffer + size_in_sector, aligned_remaining_size));
size -= aligned_remaining_size;
}
/* Read any leftover data. */
if (size) {
R_TRY(Base::Read(offset + size_in_sector + aligned_remaining_size, m_sector_buf, SectorSize));
std::memcpy(buffer + size_in_sector + aligned_remaining_size, m_sector_buf, size);
}
}
R_SUCCEED();
}
virtual Result Write(s64 offset, const void *_buffer, size_t size) override {
const u8 *buffer = static_cast<const u8 *>(_buffer);
const s64 seek = util::AlignDown(offset, SectorSize);
const s64 sector_ofs = offset - seek;
/* Check if we have nothing to do. */
if (size == 0) {
R_SUCCEED();
}
/* Fast case. */
if (sector_ofs == 0 && util::IsAligned(size, SectorSize)) {
R_RETURN(Base::Write(offset, buffer, size));
}
/* Load existing sector data. */
R_TRY(Base::Read(seek, m_sector_buf, SectorSize));
if (size + sector_ofs <= SectorSize) {
/* Staying within the sector. */
std::memcpy(m_sector_buf + sector_ofs, buffer, size);
R_TRY(Base::Write(seek, m_sector_buf, SectorSize));
} else {
/* Leaving the sector. */
const size_t size_in_sector = SectorSize - sector_ofs;
std::memcpy(m_sector_buf + sector_ofs, buffer, size_in_sector);
R_TRY(Base::Write(seek, m_sector_buf, SectorSize));
size -= size_in_sector;
/* Write as many guaranteed aligned sectors as we can. */
const size_t aligned_remaining_size = util::AlignDown(size, SectorSize);
if (aligned_remaining_size) {
R_TRY(Base::Write(offset + size_in_sector, buffer + size_in_sector, aligned_remaining_size));
size -= aligned_remaining_size;
}
/* Write any leftover data. */
if (size) {
R_TRY(Base::Read(offset + size_in_sector + aligned_remaining_size, m_sector_buf, SectorSize));
std::memcpy(m_sector_buf, buffer + size_in_sector + aligned_remaining_size, size);
R_TRY(Base::Write(offset + size_in_sector + aligned_remaining_size, m_sector_buf, SectorSize));
}
}
R_SUCCEED();
}
};
/* Represents an RCM-preserving BOOT0 partition. */
class Boot0Storage : public SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200> {
public:
using Base = SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200>;
static constexpr s64 BctEndOffset = 0xFC000;
static constexpr s64 BctSize = static_cast<s64>(ams::updater::BctSize);
static constexpr s64 BctPubkStart = 0x210;
static constexpr s64 BctPubkSize = 0x100;
static constexpr s64 BctPubkEnd = BctPubkStart + BctPubkSize;
static constexpr s64 EksStart = 0x180000;
static constexpr s64 EksSize = static_cast<s64>(ams::updater::EksSize);
static constexpr s64 EksEnd = EksStart + EksSize;
private:
sm::MitmProcessInfo m_client_info;
private:
bool CanModifyBctPublicKey();
public:
Boot0Storage(FsStorage &s, const sm::MitmProcessInfo &c) : Base(s), m_client_info(c) { /* ... */ }
public:
virtual Result Read(s64 offset, void *_buffer, size_t size) override;
virtual Result Write(s64 offset, const void *_buffer, size_t size) override;
};
class CustomPublicKeyBoot0Storage : public SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200> {
public:
using Base = SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200>;
private:
sm::MitmProcessInfo m_client_info;
spl::SocType m_soc_type;
public:
CustomPublicKeyBoot0Storage(FsStorage &s, const sm::MitmProcessInfo &c, spl::SocType soc);
public:
virtual Result Read(s64 offset, void *_buffer, size_t size) override;
virtual Result Write(s64 offset, const void *_buffer, size_t size) override;
};
bool DetectBoot0CustomPublicKey(::FsStorage &storage);
}

View File

@@ -1,136 +0,0 @@
/*
* 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 "fsmitm_calibration_binary_storage.hpp"
namespace ams::mitm::fs {
using namespace ams::fs;
namespace {
constinit os::SdkMutex g_cal0_access_mutex;
}
Result CalibrationBinaryStorage::Read(s64 offset, void *_buffer, size_t size) {
/* Acquire exclusive calibration binary access. */
std::scoped_lock lk(g_cal0_access_mutex);
/* Get u8 buffer. */
u8 *buffer = static_cast<u8 *>(_buffer);
/* Succeed on zero-size. */
R_SUCCEED_IF(size == 0);
/* Handle the blank region. */
if (m_read_blank) {
if (BlankStartOffset <= offset && offset < BlankEndOffset) {
const size_t blank_size = std::min(size, static_cast<size_t>(BlankEndOffset - offset));
mitm::ReadFromBlankCalibrationBinary(offset, buffer, blank_size);
size -= blank_size;
buffer += blank_size;
offset += blank_size;
}
}
/* Succeed if we're done. */
R_SUCCEED_IF(size == 0);
/* Handle any in-between data. */
if (BlankEndOffset <= offset && offset < FakeSecureStartOffset) {
const size_t mid_size = std::min(size, static_cast<size_t>(FakeSecureStartOffset - offset));
R_TRY(Base::Read(offset, buffer, mid_size));
size -= mid_size;
buffer += mid_size;
offset += mid_size;
}
/* Succeed if we're done. */
R_SUCCEED_IF(size == 0);
/* Handle the secure region. */
if (FakeSecureStartOffset <= offset && offset < FakeSecureEndOffset) {
const size_t fake_size = std::min(size, static_cast<size_t>(FakeSecureEndOffset - offset));
mitm::ReadFromFakeSecureBackupStorage(offset, buffer, fake_size);
size -= fake_size;
buffer += fake_size;
offset += fake_size;
}
/* Succeed if we're done. */
R_SUCCEED_IF(size == 0);
/* Handle any remaining data. */
R_RETURN(Base::Read(offset, buffer, size));
}
Result CalibrationBinaryStorage::Write(s64 offset, const void *_buffer, size_t size) {
/* Acquire exclusive calibration binary access. */
std::scoped_lock lk(g_cal0_access_mutex);
/* Get const u8 buffer. */
const u8 *buffer = static_cast<const u8 *>(_buffer);
/* Succeed on zero-size. */
R_SUCCEED_IF(size == 0);
/* Only allow writes if we should. */
R_UNLESS(m_allow_writes, fs::ResultUnsupportedOperation());
/* Handle the blank region. */
if (m_read_blank) {
if (BlankStartOffset <= offset && offset < BlankEndOffset) {
const size_t blank_size = std::min(size, static_cast<size_t>(BlankEndOffset - offset));
mitm::WriteToBlankCalibrationBinary(offset, buffer, blank_size);
size -= blank_size;
buffer += blank_size;
offset += blank_size;
}
}
/* Succeed if we're done. */
R_SUCCEED_IF(size == 0);
/* Handle any in-between data. */
if (BlankEndOffset <= offset && offset < FakeSecureStartOffset) {
const size_t mid_size = std::min(size, static_cast<size_t>(FakeSecureStartOffset - offset));
R_TRY(Base::Write(offset, buffer, mid_size));
size -= mid_size;
buffer += mid_size;
offset += mid_size;
}
/* Succeed if we're done. */
R_SUCCEED_IF(size == 0);
/* Handle the secure region. */
if (FakeSecureStartOffset <= offset && offset < FakeSecureEndOffset) {
const size_t fake_size = std::min(size, static_cast<size_t>(FakeSecureEndOffset - offset));
mitm::WriteToFakeSecureBackupStorage(offset, buffer, fake_size);
size -= fake_size;
buffer += fake_size;
offset += fake_size;
}
/* Succeed if we're done. */
R_SUCCEED_IF(size == 0);
/* Handle any remaining data. */
R_RETURN(Base::Write(offset, buffer, size));
}
}

View File

@@ -1,53 +0,0 @@
/*
* 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>
#include "fsmitm_boot0storage.hpp"
#include "../amsmitm_prodinfo_utils.hpp"
namespace ams::mitm::fs {
/* Represents a protected calibration binary partition. */
class CalibrationBinaryStorage : public SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200> {
public:
using Base = SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200>;
static constexpr s64 BlankStartOffset = 0x0;
static constexpr s64 BlankSize = static_cast<s64>(CalibrationBinarySize);
static constexpr s64 BlankEndOffset = BlankStartOffset + BlankSize;
static constexpr s64 FakeSecureStartOffset = SecureCalibrationInfoBackupOffset;
static constexpr s64 FakeSecureSize = static_cast<s64>(SecureCalibrationBinaryBackupSize);
static constexpr s64 FakeSecureEndOffset = FakeSecureStartOffset + FakeSecureSize;
private:
sm::MitmProcessInfo m_client_info;
bool m_read_blank;
bool m_allow_writes;
public:
CalibrationBinaryStorage(FsStorage &s, const sm::MitmProcessInfo &c)
: Base(s), m_client_info(c),
m_read_blank(mitm::ShouldReadBlankCalibrationBinary()),
m_allow_writes(mitm::IsWriteToCalibrationBinaryAllowed())
{
/* ... */
}
public:
virtual Result Read(s64 offset, void *_buffer, size_t size) override;
virtual Result Write(s64 offset, const void *_buffer, size_t size) override;
};
}

View File

@@ -1,432 +0,0 @@
/*
* 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 "../amsmitm_initialization.hpp"
#include "../amsmitm_fs_utils.hpp"
#include "fsmitm_layered_romfs_storage.hpp"
namespace ams::mitm::fs {
namespace {
constinit os::SdkMutex g_mq_lock;
constinit bool g_started_req_thread;
constinit uintptr_t g_mq_storage[2];
os::MessageQueue g_req_mq(g_mq_storage + 0, 1);
os::MessageQueue g_ack_mq(g_mq_storage + 1, 1);
class LayeredRomfsStorageHolder : public util::IntrusiveRedBlackTreeBaseNode<LayeredRomfsStorageHolder> {
public:
using RedBlackKeyType = u64;
private:
LayeredRomfsStorageImpl *m_impl;
u32 m_reference_count;
bool m_second_chance;
bool m_process_romfs;
public:
LayeredRomfsStorageHolder(LayeredRomfsStorageImpl *impl, bool process_rom) : m_impl(impl), m_reference_count(1), m_second_chance(true), m_process_romfs(process_rom) {
/* ... */
}
~LayeredRomfsStorageHolder() {
delete m_impl;
}
constexpr LayeredRomfsStorageImpl *GetImpl() const { return m_impl; }
constexpr ncm::ProgramId GetProgramId() const { return m_impl->GetProgramId(); }
constexpr u32 GetReferenceCount() const { return m_reference_count; }
void OpenReferenceImpl() { ++m_reference_count; }
void CloseReferenceImpl() { --m_reference_count; }
bool GetSecondChanceImpl() const { return m_second_chance; }
void SetSecondChanceImpl(bool sc) { m_second_chance = sc; }
bool IsProcessRomfs() const { return m_process_romfs; }
static constexpr ALWAYS_INLINE int Compare(const RedBlackKeyType &lval, const LayeredRomfsStorageHolder &rhs) {
const auto rval = rhs.GetProgramId().value;
if (lval < rval) {
return -1;
} else if (lval == rval) {
return 0;
} else {
return 1;
}
}
static constexpr ALWAYS_INLINE int Compare(const LayeredRomfsStorageHolder &lhs, const LayeredRomfsStorageHolder &rhs) {
return Compare(lhs.GetProgramId().value, rhs);
}
};
using LayeredRomfsStorageSet = typename util::IntrusiveRedBlackTreeBaseTraits<LayeredRomfsStorageHolder>::TreeType<LayeredRomfsStorageHolder>;
constinit os::SdkRecursiveMutex g_storage_set_mutex;
constinit LayeredRomfsStorageSet g_storage_set;
constinit os::SdkMutex g_initialization_mutex;
void OpenReference(LayeredRomfsStorageImpl *impl) {
std::scoped_lock lk(g_storage_set_mutex);
auto it = g_storage_set.find_key(impl->GetProgramId().value);
AMS_ABORT_UNLESS(it != g_storage_set.end());
it->OpenReferenceImpl();
}
void CloseReference(LayeredRomfsStorageImpl *impl) {
std::scoped_lock lk(g_storage_set_mutex);
auto it = g_storage_set.find_key(impl->GetProgramId().value);
AMS_ABORT_UNLESS(it != g_storage_set.end());
AMS_ABORT_UNLESS(it->GetReferenceCount() > 0);
it->CloseReferenceImpl();
}
void RomfsInitializerThreadFunction(void *) {
while (true) {
uintptr_t storage_uptr = 0;
g_req_mq.Receive(std::addressof(storage_uptr));
auto *impl = reinterpret_cast<LayeredRomfsStorageImpl *>(storage_uptr);
g_ack_mq.Send(storage_uptr);
std::scoped_lock lk(g_initialization_mutex);
impl->InitializeImpl();
/* Close the initial reference. */
CloseReference(impl);
}
}
void RomfsFinalizerThreadFunction(void *) {
while (true) {
{
std::scoped_lock lk(g_storage_set_mutex);
auto it = g_storage_set.begin();
while (it != g_storage_set.end()) {
if (it->GetReferenceCount() > 0) {
it->SetSecondChanceImpl(true);
++it;
} else if (it->GetSecondChanceImpl()) {
it->SetSecondChanceImpl(false);
++it;
} else {
auto *holder = std::addressof(*it);
it = g_storage_set.erase(it);
delete holder;
}
}
}
os::SleepThread(TimeSpan::FromMilliSeconds(500));
}
}
constexpr size_t RomfsInitializerThreadStackSize = 0x8000;
os::ThreadType g_romfs_initializer_thread;
os::ThreadType g_romfs_finalizer_thread;
alignas(os::ThreadStackAlignment) u8 g_romfs_initializer_thread_stack[RomfsInitializerThreadStackSize];
alignas(os::ThreadStackAlignment) u8 g_romfs_finalizer_thread_stack[os::MemoryPageSize];
void RequestInitializeStorage(uintptr_t storage_uptr) {
std::scoped_lock lk(g_mq_lock);
if (AMS_UNLIKELY(!g_started_req_thread)) {
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_romfs_initializer_thread), RomfsInitializerThreadFunction, nullptr, g_romfs_initializer_thread_stack, sizeof(g_romfs_initializer_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_fs, RomFileSystemInitializeThread)));
os::SetThreadNamePointer(std::addressof(g_romfs_initializer_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm_fs, RomFileSystemInitializeThread));
os::StartThread(std::addressof(g_romfs_initializer_thread));
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_romfs_finalizer_thread), RomfsFinalizerThreadFunction, nullptr, g_romfs_finalizer_thread_stack, sizeof(g_romfs_finalizer_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_fs, RomFileSystemInitializeThread)));
os::SetThreadNamePointer(std::addressof(g_romfs_finalizer_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm_fs, RomFileSystemFinalizeThread));
os::StartThread(std::addressof(g_romfs_finalizer_thread));
g_started_req_thread = true;
}
g_req_mq.Send(storage_uptr);
uintptr_t ack = 0;
g_ack_mq.Receive(std::addressof(ack));
AMS_ABORT_UNLESS(ack == storage_uptr);
}
class LayeredRomfsStorage : public ams::fs::IStorage {
private:
LayeredRomfsStorageImpl *m_impl;
public:
LayeredRomfsStorage(LayeredRomfsStorageImpl *impl) : m_impl(impl) {
OpenReference(m_impl);
}
virtual ~LayeredRomfsStorage() {
CloseReference(m_impl);
}
virtual Result Read(s64 offset, void *buffer, size_t size) override {
R_RETURN(m_impl->Read(offset, buffer, size));
}
virtual Result GetSize(s64 *out_size) override {
R_RETURN(m_impl->GetSize(out_size));
}
virtual Result Flush() override {
R_RETURN(m_impl->Flush());
}
virtual Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
R_RETURN(m_impl->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
}
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
/* TODO: Better result code? */
AMS_UNUSED(offset, buffer, size);
R_THROW(ams::fs::ResultUnsupportedOperation())
}
virtual Result SetSize(s64 size) override {
/* TODO: Better result code? */
AMS_UNUSED(size);
R_THROW(ams::fs::ResultUnsupportedOperation())
}
};
}
using namespace ams::fs;
std::shared_ptr<ams::fs::IStorage> GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs) {
/*Prepare to find or create a new storage. */
LayeredRomfsStorageImpl *impl = nullptr;
{
std::scoped_lock lk(g_storage_set_mutex);
/* Find an existing storage. */
if (auto it = g_storage_set.find_key(program_id.value); it != g_storage_set.end()) {
return std::make_shared<LayeredRomfsStorage>(it->GetImpl());
}
/* We don't have an existing storage. If we're creating process romfs, free any unreferenced process romfs. */
/* This should help prevent too much memory in use at any time. */
if (is_process_romfs) {
auto it = g_storage_set.begin();
while (it != g_storage_set.end()) {
if (it->GetReferenceCount() > 0 || !it->IsProcessRomfs()) {
++it;
} else {
auto *holder = std::addressof(*it);
it = g_storage_set.erase(it);
delete holder;
}
}
}
/* Create a new storage. */
{
::FsFile data_file;
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(data_file), program_id, "romfs.bin", OpenMode_Read))) {
impl = new LayeredRomfsStorageImpl(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), program_id);
} else {
impl = new LayeredRomfsStorageImpl(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, program_id);
}
}
/* Insert holder. Reference count will now be one. */
g_storage_set.insert(*(new LayeredRomfsStorageHolder(impl, is_process_romfs)));
}
/* Begin initialization. When this finishes, a decref will occur. */
AMS_ABORT_UNLESS(impl != nullptr);
impl->BeginInitialize();
/* Return a new shared storage for the impl. */
return std::make_shared<LayeredRomfsStorage>(impl);
}
void FinalizeLayeredRomfsStorage(ncm::ProgramId program_id) {
std::scoped_lock lk(g_initialization_mutex);
std::scoped_lock lk2(g_storage_set_mutex);
/* Find an existing storage. */
if (auto it = g_storage_set.find_key(program_id.value); it != g_storage_set.end()) {
/* We need to delete the process romfs. Require invariant that it is unreferenced, by this point. */
AMS_ABORT_UNLESS(it->GetReferenceCount() == 0);
auto *holder = std::addressof(*it);
it = g_storage_set.erase(it);
delete holder;
}
}
LayeredRomfsStorageImpl::LayeredRomfsStorageImpl(std::unique_ptr<IStorage> s_r, std::unique_ptr<IStorage> f_r, ncm::ProgramId pr_id) : m_storage_romfs(std::move(s_r)), m_file_romfs(std::move(f_r)), m_initialize_event(os::EventClearMode_ManualClear), m_program_id(std::move(pr_id)), m_is_initialized(false), m_started_initialize(false) {
/* ... */
}
LayeredRomfsStorageImpl::~LayeredRomfsStorageImpl() {
for (size_t i = 0; i < m_source_infos.size(); i++) {
m_source_infos[i].Cleanup();
}
}
void LayeredRomfsStorageImpl::BeginInitialize() {
AMS_ABORT_UNLESS(!m_started_initialize);
RequestInitializeStorage(reinterpret_cast<uintptr_t>(this));
m_started_initialize = true;
}
void LayeredRomfsStorageImpl::InitializeImpl() {
/* Build new virtual romfs. */
romfs::Builder builder(m_program_id);
if (mitm::IsInitialized()) {
builder.AddSdFiles();
}
if (m_file_romfs) {
builder.AddStorageFiles(m_file_romfs.get(), romfs::DataSourceType::File);
}
if (m_storage_romfs) {
builder.AddStorageFiles(m_storage_romfs.get(), romfs::DataSourceType::Storage);
}
builder.Build(std::addressof(m_source_infos));
m_is_initialized = true;
m_initialize_event.Signal();
}
Result LayeredRomfsStorageImpl::Read(s64 offset, void *buffer, size_t size) {
/* Check if we can succeed immediately. */
R_SUCCEED_IF(size == 0);
/* Ensure we're initialized. */
if (!m_is_initialized) {
m_initialize_event.Wait();
}
/* Validate offset/size. */
const s64 virt_size = this->GetSize();
R_UNLESS(offset >= 0, fs::ResultInvalidOffset());
R_UNLESS(offset < virt_size, fs::ResultInvalidOffset());
if (static_cast<size_t>(virt_size - offset) < size) {
size = static_cast<size_t>(virt_size - offset);
}
/* Find first source info via binary search. */
auto it = std::lower_bound(m_source_infos.begin(), m_source_infos.end(), offset);
u8 *cur_dst = static_cast<u8 *>(buffer);
/* Our operator < compares against start of info instead of end, so we need to subtract one from lower bound. */
it--;
size_t read_so_far = 0;
while (read_so_far < size) {
const auto &cur_source = *it;
AMS_ABORT_UNLESS(offset >= cur_source.virtual_offset);
if (offset < cur_source.virtual_offset + cur_source.size) {
const s64 offset_within_source = offset - cur_source.virtual_offset;
const size_t cur_read_size = std::min(size - read_so_far, static_cast<size_t>(cur_source.size - offset_within_source));
switch (cur_source.source_type) {
case romfs::DataSourceType::Storage:
R_ABORT_UNLESS(m_storage_romfs->Read(cur_source.storage_source_info.offset + offset_within_source, cur_dst, cur_read_size));
break;
case romfs::DataSourceType::File:
R_ABORT_UNLESS(m_file_romfs->Read(cur_source.file_source_info.offset + offset_within_source, cur_dst, cur_read_size));
break;
case romfs::DataSourceType::LooseSdFile:
{
FsFile file;
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdRomfsFile(std::addressof(file), m_program_id, cur_source.loose_source_info.path, OpenMode_Read));
ON_SCOPE_EXIT { fsFileClose(std::addressof(file)); };
u64 out_read = 0;
R_ABORT_UNLESS(fsFileRead(std::addressof(file), offset_within_source, cur_dst, cur_read_size, FsReadOption_None, std::addressof(out_read)));
AMS_ABORT_UNLESS(out_read == cur_read_size);
}
break;
case romfs::DataSourceType::Memory:
std::memcpy(cur_dst, cur_source.memory_source_info.data + offset_within_source, cur_read_size);
break;
case romfs::DataSourceType::Metadata:
{
size_t out_read = 0;
R_ABORT_UNLESS(cur_source.metadata_source_info.file->Read(std::addressof(out_read), offset_within_source, cur_dst, cur_read_size));
AMS_ABORT_UNLESS(out_read == cur_read_size);
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
read_so_far += cur_read_size;
cur_dst += cur_read_size;
offset += cur_read_size;
} else {
/* Explicitly handle padding. */
const auto &next_source = *(++it);
const size_t padding_size = static_cast<size_t>(next_source.virtual_offset - offset);
std::memset(cur_dst, 0, padding_size);
read_so_far += padding_size;
cur_dst += padding_size;
offset += padding_size;
}
}
R_SUCCEED();
}
Result LayeredRomfsStorageImpl::GetSize(s64 *out_size) {
/* Ensure we're initialized. */
if (!m_is_initialized) {
m_initialize_event.Wait();
}
*out_size = this->GetSize();
R_SUCCEED();
}
Result LayeredRomfsStorageImpl::Flush() {
R_SUCCEED();
}
Result LayeredRomfsStorageImpl::OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
AMS_UNUSED(offset, src, src_size);
switch (op_id) {
case OperationId::Invalidate:
case OperationId::QueryRange:
if (size == 0) {
if (op_id == OperationId::QueryRange) {
R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
R_UNLESS(dst_size == sizeof(QueryRangeInfo), fs::ResultInvalidSize());
reinterpret_cast<QueryRangeInfo *>(dst)->Clear();
}
R_SUCCEED();
}
/* TODO: How to deal with this? */
R_THROW(fs::ResultUnsupportedOperation());
default:
R_THROW(fs::ResultUnsupportedOperation());
}
}
}

View File

@@ -1,56 +0,0 @@
/*
* 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>
#include "fsmitm_romfs.hpp"
namespace ams::mitm::fs {
class LayeredRomfsStorageImpl {
private:
romfs::Builder::SourceInfoVector m_source_infos;
std::unique_ptr<ams::fs::IStorage> m_storage_romfs;
std::unique_ptr<ams::fs::IStorage> m_file_romfs;
os::Event m_initialize_event;
ncm::ProgramId m_program_id;
bool m_is_initialized;
bool m_started_initialize;
protected:
inline s64 GetSize() const {
const auto &back = m_source_infos.back();
return back.virtual_offset + back.size;
}
public:
LayeredRomfsStorageImpl(std::unique_ptr<ams::fs::IStorage> s_r, std::unique_ptr<ams::fs::IStorage> f_r, ncm::ProgramId pr_id);
~LayeredRomfsStorageImpl();
void BeginInitialize();
void InitializeImpl();
constexpr ncm::ProgramId GetProgramId() const { return m_program_id; }
Result Read(s64 offset, void *buffer, size_t size);
Result GetSize(s64 *out_size);
Result Flush();
Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size);
};
std::shared_ptr<ams::fs::IStorage> GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs);
void FinalizeLayeredRomfsStorage(ncm::ProgramId program_id);
}

View File

@@ -1,111 +0,0 @@
/*
* 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 "fsmitm_module.hpp"
#include "fs_mitm_service.hpp"
namespace ams::mitm::fs {
namespace {
enum PortIndex {
PortIndex_Mitm,
PortIndex_Count,
};
constexpr sm::ServiceName MitmServiceName = sm::ServiceName::Encode("fsp-srv");
struct ServerOptions {
static constexpr size_t PointerBufferSize = 0x800;
static constexpr size_t MaxDomains = 0x40;
static constexpr size_t MaxDomainObjects = 0x4000;
static constexpr bool CanDeferInvokeRequest = false;
static constexpr bool CanManageMitmServers = true;
};
constexpr size_t MaxSessions = 61;
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
private:
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
};
ServerManager g_server_manager;
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
/* Acknowledge the mitm session. */
std::shared_ptr<::Service> fsrv;
sm::MitmProcessInfo client_info;
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
switch (port_index) {
case PortIndex_Mitm:
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<IFsMitmInterface, FsMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
constexpr size_t TotalThreads = 5;
static_assert(TotalThreads >= 1, "TotalThreads");
constexpr size_t NumExtraThreads = TotalThreads - 1;
constexpr size_t ThreadStackSize = mitm::ModuleTraits<fs::MitmModule>::StackSize;
alignas(os::MemoryPageSize) u8 g_extra_thread_stacks[NumExtraThreads][ThreadStackSize];
os::ThreadType g_extra_threads[NumExtraThreads];
void LoopServerThread(void *) {
/* Loop forever, servicing our services. */
g_server_manager.LoopProcess();
}
void ProcessForServerOnAllThreads() {
/* Initialize threads. */
if constexpr (NumExtraThreads > 0) {
const s32 priority = os::GetThreadCurrentPriority(os::GetCurrentThread());
for (size_t i = 0; i < NumExtraThreads; i++) {
R_ABORT_UNLESS(os::CreateThread(g_extra_threads + i, LoopServerThread, nullptr, g_extra_thread_stacks[i], ThreadStackSize, priority));
}
}
/* Start extra threads. */
if constexpr (NumExtraThreads > 0) {
for (size_t i = 0; i < NumExtraThreads; i++) {
os::StartThread(g_extra_threads + i);
}
}
/* Loop this thread. */
LoopServerThread(nullptr);
/* Wait for extra threads to finish. */
if constexpr (NumExtraThreads > 0) {
for (size_t i = 0; i < NumExtraThreads; i++) {
os::WaitThread(g_extra_threads + i);
}
}
}
}
void MitmModule::ThreadFunction(void *) {
/* Create fs mitm. */
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<FsMitmService>(PortIndex_Mitm, MitmServiceName)));
/* Process for the server. */
ProcessForServerOnAllThreads();
}
}

View File

@@ -1,24 +0,0 @@
/*
* 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>
#include "../amsmitm_module.hpp"
namespace ams::mitm::fs {
DEFINE_MITM_MODULE_CLASS(0x8000, AMS_GET_SYSTEM_THREAD_PRIORITY(fs, WorkerThreadPool) - 1);
}

View File

@@ -1,105 +0,0 @@
/*
* 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::mitm::fs {
class ReadOnlyLayeredFileSystem : public ams::fs::fsa::IFileSystem {
private:
ams::fs::ReadOnlyFileSystem m_fs_1;
ams::fs::ReadOnlyFileSystem m_fs_2;
public:
explicit ReadOnlyLayeredFileSystem(std::unique_ptr<ams::fs::fsa::IFileSystem> a, std::unique_ptr<ams::fs::fsa::IFileSystem> b) : m_fs_1(std::move(a)), m_fs_2(std::move(b)) { /* ... */ }
virtual ~ReadOnlyLayeredFileSystem() { /* ... */ }
private:
virtual Result DoCreateFile(const ams::fs::Path &path, s64 size, int flags) override final {
AMS_UNUSED(path, size, flags);
R_THROW(ams::fs::ResultUnsupportedOperation())
}
virtual Result DoDeleteFile(const ams::fs::Path &path) override final {
AMS_UNUSED(path);
R_THROW(ams::fs::ResultUnsupportedOperation())
}
virtual Result DoCreateDirectory(const ams::fs::Path &path) override final {
AMS_UNUSED(path);
R_THROW(ams::fs::ResultUnsupportedOperation())
}
virtual Result DoDeleteDirectory(const ams::fs::Path &path) override final {
AMS_UNUSED(path);
R_THROW(ams::fs::ResultUnsupportedOperation())
}
virtual Result DoDeleteDirectoryRecursively(const ams::fs::Path &path) override final {
AMS_UNUSED(path);
R_THROW(ams::fs::ResultUnsupportedOperation())
}
virtual Result DoRenameFile(const ams::fs::Path &old_path, const ams::fs::Path &new_path) override final {
AMS_UNUSED(old_path, new_path);
R_THROW(ams::fs::ResultUnsupportedOperation())
}
virtual Result DoRenameDirectory(const ams::fs::Path &old_path, const ams::fs::Path &new_path) override final {
AMS_UNUSED(old_path, new_path);
R_THROW(ams::fs::ResultUnsupportedOperation())
}
virtual Result DoGetEntryType(ams::fs::DirectoryEntryType *out, const ams::fs::Path &path) override final {
R_SUCCEED_IF(R_SUCCEEDED(m_fs_1.GetEntryType(out, path)));
R_RETURN(m_fs_2.GetEntryType(out, path));
}
virtual Result DoOpenFile(std::unique_ptr<ams::fs::fsa::IFile> *out_file, const ams::fs::Path &path, ams::fs::OpenMode mode) override final {
R_SUCCEED_IF(R_SUCCEEDED(m_fs_1.OpenFile(out_file, path, mode)));
R_RETURN(m_fs_2.OpenFile(out_file, path, mode));
}
virtual Result DoOpenDirectory(std::unique_ptr<ams::fs::fsa::IDirectory> *out_dir, const ams::fs::Path &path, ams::fs::OpenDirectoryMode mode) override final {
R_SUCCEED_IF(R_SUCCEEDED(m_fs_1.OpenDirectory(out_dir, path, mode)));
R_RETURN(m_fs_2.OpenDirectory(out_dir, path, mode));
}
virtual Result DoCommit() override final {
R_SUCCEED();
}
virtual Result DoGetFreeSpaceSize(s64 *out, const ams::fs::Path &path) {
AMS_UNUSED(out, path);
R_THROW(ams::fs::ResultUnsupportedOperation())
}
virtual Result DoGetTotalSpaceSize(s64 *out, const ams::fs::Path &path) {
AMS_UNUSED(out, path);
R_THROW(ams::fs::ResultUnsupportedOperation())
}
virtual Result DoCleanDirectoryRecursively(const ams::fs::Path &path) {
AMS_UNUSED(path);
R_THROW(ams::fs::ResultUnsupportedOperation())
}
virtual Result DoGetFileTimeStampRaw(ams::fs::FileTimeStampRaw *out, const ams::fs::Path &path) {
R_SUCCEED_IF(R_SUCCEEDED(m_fs_1.GetFileTimeStampRaw(out, path)));
R_RETURN(m_fs_2.GetFileTimeStampRaw(out, path));
}
};
}

View File

@@ -1,379 +0,0 @@
/*
* 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::mitm::fs::romfs {
enum class DataSourceType : u8 {
Storage,
File,
LooseSdFile,
Metadata,
Memory,
};
enum AllocationType {
AllocationType_FileName,
AllocationType_DirName,
AllocationType_FullPath,
AllocationType_SourceInfo,
AllocationType_BuildFileContext,
AllocationType_BuildDirContext,
AllocationType_TableCache,
AllocationType_DirPointerArray,
AllocationType_DirContextSet,
AllocationType_FileContextSet,
AllocationType_Memory,
AllocationType_Count,
};
void *AllocateTracked(AllocationType type, size_t size);
void FreeTracked(AllocationType type, void *p, size_t size);
template<typename T, typename... Args>
T *AllocateTyped(AllocationType type, Args &&... args) {
void *mem = AllocateTracked(type, sizeof(T));
return std::construct_at(static_cast<T *>(mem), std::forward<Args>(args)...);
}
template<AllocationType AllocType, typename T>
class TrackedAllocator {
public:
using value_type = T;
template<typename U>
struct rebind {
using other = TrackedAllocator<AllocType, U>;
};
public:
TrackedAllocator() = default;
T *allocate(size_t n) {
return static_cast<T *>(AllocateTracked(AllocType, sizeof(T) * n));
}
void deallocate(T *p, size_t n) {
FreeTracked(AllocType, p, sizeof(T) * n);
}
};
struct SourceInfo {
s64 virtual_offset;
s64 size;
union {
struct {
s64 offset;
} storage_source_info;
struct {
s64 offset;
} file_source_info;
struct {
char *path;
} loose_source_info;
struct {
ams::fs::fsa::IFile *file;
} metadata_source_info;
struct {
u8 *data;
} memory_source_info;
};
DataSourceType source_type;
bool cleaned_up;
SourceInfo(s64 v_o, s64 sz, DataSourceType type, s64 p_o) : virtual_offset(v_o), size(sz), source_type(type), cleaned_up(false) {
switch (this->source_type) {
case DataSourceType::Storage:
this->storage_source_info.offset = p_o;
break;
case DataSourceType::File:
this->file_source_info.offset = p_o;
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
SourceInfo(s64 v_o, s64 sz, DataSourceType type, void *arg) : virtual_offset(v_o), size(sz), source_type(type), cleaned_up(false) {
switch (this->source_type) {
case DataSourceType::LooseSdFile:
this->loose_source_info.path = static_cast<char *>(arg);
break;
case DataSourceType::Metadata:
this->metadata_source_info.file = static_cast<ams::fs::fsa::IFile *>(arg);
break;
case DataSourceType::Memory:
this->memory_source_info.data = static_cast<u8 *>(arg);
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
void Cleanup() {
AMS_ABORT_UNLESS(!this->cleaned_up);
this->cleaned_up = true;
switch (this->source_type) {
case DataSourceType::Storage:
case DataSourceType::File:
break;
case DataSourceType::Metadata:
delete this->metadata_source_info.file;
break;
case DataSourceType::LooseSdFile:
FreeTracked(AllocationType_FullPath, this->loose_source_info.path, std::strlen(this->loose_source_info.path) + 1);
break;
case DataSourceType::Memory:
FreeTracked(AllocationType_Memory, this->memory_source_info.data, this->size);
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
};
constexpr inline bool operator<(const SourceInfo &lhs, const SourceInfo &rhs) {
return lhs.virtual_offset < rhs.virtual_offset;
}
constexpr inline bool operator<(const SourceInfo &lhs, const s64 rhs) {
return lhs.virtual_offset <= rhs;
}
struct BuildFileContext;
struct BuildDirectoryContext {
NON_COPYABLE(BuildDirectoryContext);
NON_MOVEABLE(BuildDirectoryContext);
char *path;
union {
BuildDirectoryContext *parent;
};
union {
BuildDirectoryContext *child;
struct {
u32 parent_offset;
u32 child_offset;
};
};
union {
BuildDirectoryContext *sibling;
u32 sibling_offset;
};
union {
BuildFileContext *file;
u32 file_offset;
};
u32 path_len;
u32 entry_offset;
u32 hash_value;
struct RootTag{};
BuildDirectoryContext(RootTag) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), path_len(0), entry_offset(0), hash_value(0xFFFFFFFF) {
this->path = static_cast<char *>(AllocateTracked(AllocationType_DirName, 1));
this->path[0] = '\x00';
}
BuildDirectoryContext(const char *entry_name, size_t entry_name_len) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), entry_offset(0) {
this->path_len = entry_name_len;
this->path = static_cast<char *>(AllocateTracked(AllocationType_DirName, this->path_len + 1));
std::memcpy(this->path, entry_name, entry_name_len);
this->path[this->path_len] = '\x00';
}
~BuildDirectoryContext() {
if (this->path != nullptr) {
FreeTracked(AllocationType_DirName, this->path, this->path_len + 1);
this->path = nullptr;
}
}
void operator delete(void *p) {
FreeTracked(AllocationType_BuildDirContext, p, sizeof(BuildDirectoryContext));
}
size_t GetPathLength() const {
if (this->parent == nullptr) {
return 0;
}
return this->parent->GetPathLength() + 1 + this->path_len;
}
size_t GetPath(char *dst) const {
if (this->parent == nullptr) {
dst[0] = '\x00';
return 0;
}
const size_t parent_len = this->parent->GetPath(dst);
dst[parent_len] = '/';
std::memcpy(dst + parent_len + 1, this->path, this->path_len);
dst[parent_len + 1 + this->path_len] = '\x00';
return parent_len + 1 + this->path_len;
}
bool HasHashMark() const {
return reinterpret_cast<uintptr_t>(this->sibling) & UINT64_C(0x8000000000000000);
}
void SetHashMark() {
this->sibling = reinterpret_cast<BuildDirectoryContext *>(reinterpret_cast<uintptr_t>(this->sibling) | UINT64_C(0x8000000000000000));
}
void ClearHashMark() {
this->sibling = reinterpret_cast<BuildDirectoryContext *>(reinterpret_cast<uintptr_t>(this->sibling) & ~UINT64_C(0x8000000000000000));
}
};
struct BuildFileContext {
NON_COPYABLE(BuildFileContext);
NON_MOVEABLE(BuildFileContext);
char *path;
BuildDirectoryContext *parent;
union {
BuildFileContext *sibling;
u32 sibling_offset;
};
s64 offset;
s64 size;
s64 orig_offset;
u32 path_len;
u32 entry_offset;
u32 hash_value;
DataSourceType source_type;
BuildFileContext(const char *entry_name, size_t entry_name_len, s64 sz, s64 o_o, DataSourceType type) : parent(nullptr), sibling(nullptr), offset(0), size(sz), orig_offset(o_o), entry_offset(0), hash_value(0xFFFFFFFF), source_type(type) {
this->path_len = entry_name_len;
this->path = static_cast<char *>(AllocateTracked(AllocationType_FileName, this->path_len + 1));
std::memcpy(this->path, entry_name, entry_name_len);
this->path[this->path_len] = 0;
}
~BuildFileContext() {
if (this->path != nullptr) {
FreeTracked(AllocationType_FileName, this->path, this->path_len + 1);
this->path = nullptr;
}
}
void operator delete(void *p) {
FreeTracked(AllocationType_BuildFileContext, p, sizeof(BuildFileContext));
}
size_t GetPathLength() const {
if (this->parent == nullptr) {
return 0;
}
return this->parent->GetPathLength() + 1 + this->path_len;
}
size_t GetPath(char *dst) const {
if (this->parent == nullptr) {
dst[0] = '\x00';
return 0;
}
const size_t parent_len = this->parent->GetPath(dst);
dst[parent_len] = '/';
std::memcpy(dst + parent_len + 1, this->path, this->path_len);
dst[parent_len + 1 + this->path_len] = '\x00';
return parent_len + 1 + this->path_len;
}
bool HasHashMark() const {
return reinterpret_cast<uintptr_t>(this->sibling) & UINT64_C(0x8000000000000000);
}
void SetHashMark() {
this->sibling = reinterpret_cast<BuildFileContext *>(reinterpret_cast<uintptr_t>(this->sibling) | UINT64_C(0x8000000000000000));
}
void ClearHashMark() {
this->sibling = reinterpret_cast<BuildFileContext *>(reinterpret_cast<uintptr_t>(this->sibling) & ~UINT64_C(0x8000000000000000));
}
};
class DirectoryTableReader;
class FileTableReader;
class Builder {
NON_COPYABLE(Builder);
NON_MOVEABLE(Builder);
public:
using SourceInfoVector = std::vector<SourceInfo, TrackedAllocator<AllocationType_SourceInfo, SourceInfo>>;
private:
template<typename T>
struct Comparator {
static constexpr inline int Compare(const char *a, const char *b) {
unsigned char c1{}, c2{};
while ((c1 = *a++) == (c2 = *b++)) {
if (c1 == '\x00') {
return 0;
}
}
return (c1 - c2);
}
constexpr bool operator()(const std::unique_ptr<T> &lhs, const std::unique_ptr<T> &rhs) const {
char lhs_path[ams::fs::EntryNameLengthMax + 1];
char rhs_path[ams::fs::EntryNameLengthMax + 1];
lhs->GetPath(lhs_path);
rhs->GetPath(rhs_path);
return Comparator::Compare(lhs_path, rhs_path) < 0;
}
};
template<AllocationType AllocType, typename T>
using ContextSet = std::set<std::unique_ptr<T>, Comparator<T>, TrackedAllocator<AllocType, std::unique_ptr<T>>>;
private:
ncm::ProgramId m_program_id;
BuildDirectoryContext *m_root;
ContextSet<AllocationType_DirContextSet, BuildDirectoryContext> m_directories;
ContextSet<AllocationType_FileContextSet, BuildFileContext> m_files;
size_t m_num_dirs;
size_t m_num_files;
size_t m_dir_table_size;
size_t m_file_table_size;
size_t m_dir_hash_table_size;
size_t m_file_hash_table_size;
size_t m_file_partition_size;
::FsDirectoryEntry m_dir_entry;
DataSourceType m_cur_source_type;
private:
void VisitDirectory(FsFileSystem *fs, BuildDirectoryContext *parent);
void VisitDirectory(BuildDirectoryContext *parent, u32 parent_offset, DirectoryTableReader &dir_table, FileTableReader &file_table);
void AddDirectory(BuildDirectoryContext **out, BuildDirectoryContext *parent_ctx, std::unique_ptr<BuildDirectoryContext> file_ctx);
void AddFile(BuildDirectoryContext *parent_ctx, std::unique_ptr<BuildFileContext> file_ctx);
public:
Builder(ncm::ProgramId pr_id);
~Builder();
void AddSdFiles();
void AddStorageFiles(ams::fs::IStorage *storage, DataSourceType source_type);
void Build(SourceInfoVector *out_infos);
};
Result ConfigureDynamicHeap(u64 *out_size, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application);
}

View File

@@ -1,114 +0,0 @@
/*
* 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 "fsmitm_save_utils.hpp"
namespace ams::mitm::fs {
using namespace ams::fs;
namespace {
Result GetSaveDataSpaceIdString(const char **out_str, u8 space_id) {
switch (static_cast<SaveDataSpaceId>(space_id)) {
case SaveDataSpaceId::System:
case SaveDataSpaceId::ProperSystem:
*out_str = "sys";
break;
case SaveDataSpaceId::User:
*out_str = "user";
break;
case SaveDataSpaceId::SdSystem:
*out_str = "sd_sys";
break;
case SaveDataSpaceId::Temporary:
*out_str = "temp";
break;
case SaveDataSpaceId::SdUser:
*out_str = "sd_user";
break;
case SaveDataSpaceId::SafeMode:
*out_str = "safe";
break;
default:
R_THROW(fs::ResultInvalidSaveDataSpaceId());
}
R_SUCCEED();
}
Result GetSaveDataTypeString(const char **out_str, SaveDataType save_data_type) {
switch (save_data_type) {
case SaveDataType::System:
*out_str = "system";
break;
case SaveDataType::Account:
*out_str = "account";
break;
case SaveDataType::Bcat:
*out_str = "bcat";
break;
case SaveDataType::Device:
*out_str = "device";
break;
case SaveDataType::Temporary:
*out_str = "temp";
break;
case SaveDataType::Cache:
*out_str = "cache";
break;
case SaveDataType::SystemBcat:
*out_str = "system_bcat";
break;
default:
/* TODO: Better result? */
R_THROW(fs::ResultInvalidArgument());
}
R_SUCCEED();
}
constexpr inline bool IsEmptyAccountId(const UserId &uid) {
return uid == InvalidUserId;
}
}
Result SaveUtil::GetDirectorySaveDataPath(char *dst, size_t dst_size, ncm::ProgramId program_id, u8 space_id, const fs::SaveDataAttribute &attribute) {
/* Saves should be separate for emunand vs sysnand. */
const char *emummc_str = emummc::IsActive() ? "emummc" : "sysmmc";
/* Get space_id, save_data_type strings. */
const char *space_id_str, *save_type_str;
R_TRY(GetSaveDataSpaceIdString(&space_id_str, space_id));
R_TRY(GetSaveDataTypeString(&save_type_str, attribute.type));
/* Initialize the path. */
const bool is_system = attribute.system_save_data_id != InvalidSystemSaveDataId && IsEmptyAccountId(attribute.user_id);
size_t out_path_len;
if (is_system) {
out_path_len = static_cast<size_t>(util::SNPrintf(dst, dst_size, "/atmosphere/saves/%s/%s/%s/%016lx", emummc_str, space_id_str, save_type_str, attribute.system_save_data_id));
} else {
out_path_len = static_cast<size_t>(util::SNPrintf(dst, dst_size, "/atmosphere/saves/%s/%s/%s/%016lx/%016lx%016lx", emummc_str, space_id_str, save_type_str, static_cast<u64>(program_id), attribute.user_id.data[1], attribute.user_id.data[0]));
}
R_UNLESS(out_path_len < dst_size, fs::ResultTooLongPath());
R_SUCCEED();
}
}

View File

@@ -1,26 +0,0 @@
/*
* 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::mitm::fs {
class SaveUtil {
public:
static Result GetDirectorySaveDataPath(char *dst, size_t dst_size, ncm::ProgramId program_id, u8 space_id, const ams::fs::SaveDataAttribute &attribute);
};
}

View File

@@ -1,41 +0,0 @@
/*
* 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/>.
*/
#define NX_SERVICE_ASSUME_NON_DOMAIN
#include "service_guard.h"
#include "memlet.h"
static Service g_memletSrv;
NX_GENERATE_SERVICE_GUARD(memlet);
Result _memletInitialize(void) {
return smGetService(&g_memletSrv, "memlet");
}
void _memletCleanup(void) {
serviceClose(&g_memletSrv);
}
Service* memletGetServiceSession(void) {
return &g_memletSrv;
}
Result memletCreateAppletSharedMemory(Handle *out_shmem_h, u64 *out_size, u64 desired_size) {
return serviceDispatchInOut(&g_memletSrv, 65000, desired_size, *out_size,
.out_handle_attrs = { SfOutHandleAttr_HipcMove },
.out_handles = out_shmem_h,
);
}

View File

@@ -1,32 +0,0 @@
/*
* 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 <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
Result memletInitialize(void);
void memletExit(void);
Service* memletGetServiceSession(void);
Result memletCreateAppletSharedMemory(Handle *out_shmem_h, u64 *out_size, u64 desired_size);
#ifdef __cplusplus
}
#endif

View File

@@ -1,65 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <switch/types.h>
#include <switch/result.h>
#include <switch/kernel/mutex.h>
#include <switch/sf/service.h>
#include <switch/services/sm.h>
typedef struct ServiceGuard {
Mutex mutex;
u32 refCount;
} ServiceGuard;
NX_INLINE bool serviceGuardBeginInit(ServiceGuard* g)
{
mutexLock(&g->mutex);
return (g->refCount++) == 0;
}
NX_INLINE Result serviceGuardEndInit(ServiceGuard* g, Result rc, void (*cleanupFunc)(void))
{
if (R_FAILED(rc)) {
cleanupFunc();
--g->refCount;
}
mutexUnlock(&g->mutex);
return rc;
}
NX_INLINE void serviceGuardExit(ServiceGuard* g, void (*cleanupFunc)(void))
{
mutexLock(&g->mutex);
if (g->refCount && (--g->refCount) == 0)
cleanupFunc();
mutexUnlock(&g->mutex);
}
#define NX_GENERATE_SERVICE_GUARD_PARAMS(name, _paramdecl, _parampass) \
\
static ServiceGuard g_##name##Guard; \
NX_INLINE Result _##name##Initialize _paramdecl; \
static void _##name##Cleanup(void); \
\
Result name##Initialize _paramdecl \
{ \
Result rc = 0; \
if (serviceGuardBeginInit(&g_##name##Guard)) \
rc = _##name##Initialize _parampass; \
return serviceGuardEndInit(&g_##name##Guard, rc, _##name##Cleanup); \
} \
\
void name##Exit(void) \
{ \
serviceGuardExit(&g_##name##Guard, _##name##Cleanup); \
}
#define NX_GENERATE_SERVICE_GUARD(name) NX_GENERATE_SERVICE_GUARD_PARAMS(name, (void), ())
#ifdef __cplusplus
}
#endif

View File

@@ -1,45 +0,0 @@
/*
* 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 "../amsmitm_initialization.hpp"
#include "mitm_pm_module.hpp"
#include "mitm_pm_service.hpp"
namespace ams::mitm::pm {
namespace {
constexpr sm::ServiceName PmServiceName = sm::ServiceName::Encode("mitm:pm");
constexpr size_t PmMaxSessions = 1;
constexpr size_t MaxServers = 1;
constexpr size_t MaxSessions = PmMaxSessions;
using ServerOptions = sf::hipc::DefaultServerManagerOptions;
sf::hipc::ServerManager<MaxServers, ServerOptions, MaxSessions> g_server_manager;
constinit sf::UnmanagedServiceObject<mitm::pm::impl::IPmInterface, mitm::pm::PmService> g_pm_service_object;
}
void MitmModule::ThreadFunction(void *) {
/* Create bpc:ams. */
R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_pm_service_object.GetShared(), PmServiceName, PmMaxSessions));
/* Loop forever, servicing our services. */
g_server_manager.LoopProcess();
}
}

View File

@@ -1,24 +0,0 @@
/*
* 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>
#include "../amsmitm_module.hpp"
namespace ams::mitm::pm {
DEFINE_MITM_MODULE_CLASS(0x1000, AMS_GET_SYSTEM_THREAD_PRIORITY(fs, WorkerThreadPool) - 2);
}

View File

@@ -1,35 +0,0 @@
/*
* 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 "../amsmitm_initialization.hpp"
#include "mitm_pm_service.hpp"
#include "mitm_pm_service.hpp"
#include "../fs_mitm/fsmitm_romfs.hpp"
namespace ams::mitm::pm {
Result PmService::PrepareLaunchProgram(sf::Out<u64> out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application) {
/* Default to zero heap. */
*out = 0;
/* Actually configure the required boost size for romfs. */
R_TRY(mitm::fs::romfs::ConfigureDynamicHeap(out.GetPointer(), program_id, status, is_application));
/* TODO: Is there anything else we should do, while we have the opportunity? */
R_SUCCEED();
}
}

View File

@@ -1,27 +0,0 @@
/*
* 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::mitm::pm {
class PmService {
public:
Result PrepareLaunchProgram(sf::Out<u64> out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application);
};
static_assert(impl::IsIPmInterface<PmService>);
}

View File

@@ -1,41 +0,0 @@
/*
* 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 "../amsmitm_fs_utils.hpp"
#include "ns_am_mitm_service.hpp"
#include "ns_shim.h"
namespace ams::mitm::ns {
Result NsAmMitmService::GetApplicationContentPath(const sf::OutBuffer &out_path, ncm::ProgramId application_id, u8 content_type) {
R_RETURN(nsamGetApplicationContentPathFwd(m_forward_service.get(), out_path.GetPointer(), out_path.GetSize(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type)));
}
Result NsAmMitmService::ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type) {
/* Always succeed for web applets asking about HBL to enable hbl_html, and applications with manual_html to allow custom manual data. */
bool is_hbl = false;
if ((R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast<ncm::ContentType>(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) {
nsamResolveApplicationContentPathFwd(m_forward_service.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type));
R_SUCCEED();
}
R_RETURN(nsamResolveApplicationContentPathFwd(m_forward_service.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type)));
}
Result NsAmMitmService::GetRunningApplicationProgramId(sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id) {
R_RETURN(nsamGetRunningApplicationProgramIdFwd(m_forward_service.get(), reinterpret_cast<u64 *>(out.GetPointer()), static_cast<u64>(application_id)));
}
}

View File

@@ -1,46 +0,0 @@
/*
* 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>
#define AMS_NS_AM_MITM_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 21, Result, GetApplicationContentPath, (const sf::OutBuffer &out_path, ncm::ProgramId application_id, u8 content_type), (out_path, application_id, content_type)) \
AMS_SF_METHOD_INFO(C, H, 23, Result, ResolveApplicationContentPath, (ncm::ProgramId application_id, u8 content_type), (application_id, content_type)) \
AMS_SF_METHOD_INFO(C, H, 92, Result, GetRunningApplicationProgramId, (sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id), (out, application_id), hos::Version_6_0_0)
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::ns::impl, IAmMitmInterface, AMS_NS_AM_MITM_INTERFACE_INFO, 0x059D2C39)
namespace ams::mitm::ns {
class NsAmMitmService : public sf::MitmServiceImplBase {
public:
using MitmServiceImplBase::MitmServiceImplBase;
public:
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
/* We will mitm:
* - web applets, to facilitate hbl web browser launching.
*/
return ncm::IsWebAppletId(client_info.program_id);
}
public:
/* Actual command API. */
Result GetApplicationContentPath(const sf::OutBuffer &out_path, ncm::ProgramId application_id, u8 content_type);
Result ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type);
Result GetRunningApplicationProgramId(sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id);
};
static_assert(impl::IsIAmMitmInterface<NsAmMitmService>);
}

View File

@@ -1,126 +0,0 @@
/*
* 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 <string.h>
#include <switch.h>
#include "ns_shim.h"
/* Command forwarders. */
Result nsGetDocumentInterfaceFwd(Service* s, NsDocumentInterface* out) {
return serviceDispatch(s, 7999,
.out_num_objects = 1,
.out_objects = &out->s,
);
}
static Result _nsGetApplicationContentPathOld(Service *s, void* out, size_t out_size, u64 app_id, NcmContentType content_type) {
const struct {
u8 content_type;
u64 app_id;
} in = { content_type, app_id };
return serviceDispatchIn(s, 21, in,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { out, out_size } },
);
}
static Result _nsGetApplicationContentPath(Service *s, void* out, size_t out_size, u8 *out_attr, u64 app_id, NcmContentType content_type) {
const struct {
u8 content_type;
u64 app_id;
} in = { content_type, app_id };
return serviceDispatchInOut(s, 21, in, *out_attr,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { out, out_size } },
);
}
static Result _nsGetApplicationContentPath2(Service *s, void* out_path, size_t out_size, u64* out_program_id, u8 *out_attr, u64 app_id, NcmContentType content_type) {
const struct {
u8 content_type;
u64 app_id;
} in = { content_type, app_id };
struct {
u8 attr;
u64 program_id;
} out;
Result rc = serviceDispatchInOut(s, 2524, in, out,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { out_path, out_size } },
);
if (R_SUCCEEDED(rc)) {
*out_program_id = out.program_id;
*out_attr = out.attr;
}
return rc;
}
static Result _nsResolveApplicationContentPath(Service* s, u64 app_id, NcmContentType content_type) {
const struct {
u8 content_type;
u64 app_id;
} in = { content_type, app_id };
return serviceDispatchIn(s, 23, in);
}
static Result _nsGetRunningApplicationProgramId(Service* s, u64* out_program_id, u64 app_id) {
if (hosversionBefore(6, 0, 0)) {
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
}
return serviceDispatchInOut(s, 92, app_id, *out_program_id);
}
/* Application Manager forwarders. */
Result nsamGetApplicationContentPathFwd(Service* s, void* out, size_t out_size, u64 app_id, NcmContentType content_type) {
return _nsGetApplicationContentPathOld(s, out, out_size, app_id, content_type);
}
Result nsamResolveApplicationContentPathFwd(Service* s, u64 app_id, NcmContentType content_type) {
return _nsResolveApplicationContentPath(s, app_id, content_type);
}
Result nsamGetRunningApplicationProgramIdFwd(Service* s, u64* out_program_id, u64 app_id) {
return _nsGetRunningApplicationProgramId(s, out_program_id, app_id);
}
/* Web forwarders */
Result nswebGetApplicationContentPath(NsDocumentInterface* doc, void* out, size_t out_size, u8 *out_attr, u64 app_id, NcmContentType content_type) {
if (hosversionAtLeast(16,0,0)) {
return _nsGetApplicationContentPath(&doc->s, out, out_size, out_attr, app_id, content_type);
} else {
return _nsGetApplicationContentPathOld(&doc->s, out, out_size, app_id, content_type);
}
}
Result nswebResolveApplicationContentPath(NsDocumentInterface* doc, u64 app_id, NcmContentType content_type) {
return _nsResolveApplicationContentPath(&doc->s, app_id, content_type);
}
Result nswebGetRunningApplicationProgramId(NsDocumentInterface* doc, u64* out_program_id, u64 app_id) {
return _nsGetRunningApplicationProgramId(&doc->s, out_program_id, app_id);
}
Result nswebGetApplicationContentPath2(NsDocumentInterface* doc, void* out, size_t out_size, u64* out_program_id, u8 *out_attr, u64 app_id, NcmContentType content_type) {
return _nsGetApplicationContentPath2(&doc->s, out, out_size, out_program_id, out_attr, app_id, content_type);
}
void nsDocumentInterfaceClose(NsDocumentInterface* doc) {
serviceClose(&doc->s);
}

View File

@@ -1,34 +0,0 @@
/**
* @file ns_shim.h
* @brief Nintendo Shell Services (ns) IPC wrapper.
* @author SciresM
* @copyright libnx Authors
*/
#pragma once
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
Service s;
} NsDocumentInterface;
/* Command forwarders. */
Result nsGetDocumentInterfaceFwd(Service* s, NsDocumentInterface* out);
Result nsamGetApplicationContentPathFwd(Service* s, void* out, size_t out_size, u64 app_id, NcmContentType content_type);
Result nsamResolveApplicationContentPathFwd(Service* s, u64 app_id, NcmContentType content_type);
Result nsamGetRunningApplicationProgramIdFwd(Service* s, u64* out_program_id, u64 app_id);
Result nswebGetApplicationContentPath(NsDocumentInterface* doc, void* out, size_t out_size, u8 *out_attr, u64 app_id, NcmContentType content_type);
Result nswebResolveApplicationContentPath(NsDocumentInterface* doc, u64 app_id, NcmContentType content_type);
Result nswebGetRunningApplicationProgramId(NsDocumentInterface* doc, u64* out_program_id, u64 app_id);
Result nswebGetApplicationContentPath2(NsDocumentInterface* doc, void* out, size_t out_size, u64* out_program_id, u8 *out_attr, u64 app_id, NcmContentType content_type);
void nsDocumentInterfaceClose(NsDocumentInterface* doc);
#ifdef __cplusplus
}
#endif

View File

@@ -1,56 +0,0 @@
/*
* 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 "../amsmitm_fs_utils.hpp"
#include "ns_web_mitm_service.hpp"
namespace ams::mitm::ns {
Result NsDocumentService::GetApplicationContentPath(const sf::OutBuffer &out_path, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type) {
static_assert(sizeof(*out_attr.GetPointer()) == sizeof(u8));
R_RETURN(nswebGetApplicationContentPath(m_srv.get(), out_path.GetPointer(), out_path.GetSize(), reinterpret_cast<u8 *>(out_attr.GetPointer()), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type)));
}
Result NsDocumentService::ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type) {
/* Always succeed for web applets asking about HBL to enable hbl_html, and applications with manual_html to allow custom manual data. */
bool is_hbl = false;
if ((R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast<ncm::ContentType>(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) {
nswebResolveApplicationContentPath(m_srv.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type));
R_SUCCEED();
}
R_RETURN(nswebResolveApplicationContentPath(m_srv.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type)));
}
Result NsDocumentService::GetRunningApplicationProgramId(sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id) {
R_RETURN(nswebGetRunningApplicationProgramId(m_srv.get(), reinterpret_cast<u64 *>(out.GetPointer()), static_cast<u64>(application_id)));
}
Result NsDocumentService::GetApplicationContentPath2(const sf::OutBuffer &out_path, sf::Out<ncm::ProgramId> out_program_id, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type) {
static_assert(sizeof(*out_attr.GetPointer()) == sizeof(u8));
R_RETURN(nswebGetApplicationContentPath2(m_srv.get(), out_path.GetPointer(), out_path.GetSize(), reinterpret_cast<u64 *>(out_program_id.GetPointer()), reinterpret_cast<u8 *>(out_attr.GetPointer()), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type)));
}
Result NsWebMitmService::GetDocumentInterface(sf::Out<sf::SharedPointer<impl::IDocumentInterface>> out) {
/* Open a document interface. */
NsDocumentInterface doc;
R_TRY(nsGetDocumentInterfaceFwd(m_forward_service.get(), std::addressof(doc)));
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(doc.s))};
out.SetValue(sf::CreateSharedObjectEmplaced<impl::IDocumentInterface, NsDocumentService>(m_client_info, std::make_unique<NsDocumentInterface>(doc)), target_object_id);
R_SUCCEED();
}
}

View File

@@ -1,70 +0,0 @@
/*
* 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>
#include "ns_shim.h"
#define AMS_NS_DOCUMENT_MITM_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 21, Result, GetApplicationContentPath, (const sf::OutBuffer &out_path, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type), (out_path, out_attr, application_id, content_type)) \
AMS_SF_METHOD_INFO(C, H, 23, Result, ResolveApplicationContentPath, (ncm::ProgramId application_id, u8 content_type), (application_id, content_type)) \
AMS_SF_METHOD_INFO(C, H, 92, Result, GetRunningApplicationProgramId, (sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id), (out, application_id), hos::Version_6_0_0) \
AMS_SF_METHOD_INFO(C, H, 2524, Result, GetApplicationContentPath2, (const sf::OutBuffer &out_path, sf::Out<ncm::ProgramId> out_program_id, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type), (out_path, out_program_id, out_attr, application_id, content_type), hos::Version_19_0_0)
AMS_SF_DEFINE_INTERFACE(ams::mitm::ns::impl, IDocumentInterface, AMS_NS_DOCUMENT_MITM_INTERFACE_INFO, 0x0F9B1C00)
#define AMS_NS_WEB_MITM_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 7999, Result, GetDocumentInterface, (sf::Out<sf::SharedPointer<mitm::ns::impl::IDocumentInterface>> out), (out))
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::ns::impl, IWebMitmInterface, AMS_NS_WEB_MITM_INTERFACE_INFO, 0xF4EC2D1A)
namespace ams::mitm::ns {
class NsDocumentService {
private:
sm::MitmProcessInfo m_client_info;
std::unique_ptr<::NsDocumentInterface> m_srv;
public:
NsDocumentService(const sm::MitmProcessInfo &cl, std::unique_ptr<::NsDocumentInterface> s) : m_client_info(cl), m_srv(std::move(s)) { /* .. */ }
virtual ~NsDocumentService() {
nsDocumentInterfaceClose(m_srv.get());
}
public:
/* Actual command API. */
Result GetApplicationContentPath(const sf::OutBuffer &out_path, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type);
Result ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type);
Result GetRunningApplicationProgramId(sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id);
Result GetApplicationContentPath2(const sf::OutBuffer &out_path, sf::Out<ncm::ProgramId> out_program_id, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type);
};
static_assert(impl::IsIDocumentInterface<NsDocumentService>);
class NsWebMitmService : public sf::MitmServiceImplBase {
public:
using MitmServiceImplBase::MitmServiceImplBase;
public:
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
/* We will mitm:
* - web applets, to facilitate hbl web browser launching.
*/
return ncm::IsWebAppletId(client_info.program_id);
}
public:
Result GetDocumentInterface(sf::Out<sf::SharedPointer<impl::IDocumentInterface>> out);
};
static_assert(impl::IsIWebMitmInterface<NsWebMitmService>);
}

View File

@@ -1,86 +0,0 @@
/*
* 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 "../amsmitm_initialization.hpp"
#include "nsmitm_module.hpp"
#include "ns_am_mitm_service.hpp"
#include "ns_web_mitm_service.hpp"
namespace ams::mitm::ns {
namespace {
enum PortIndex {
PortIndex_Mitm,
PortIndex_Count,
};
constexpr sm::ServiceName NsAmMitmServiceName = sm::ServiceName::Encode("ns:am");
constexpr sm::ServiceName NsWebMitmServiceName = sm::ServiceName::Encode("ns:web");
constexpr size_t MaxSessions = 5;
struct ServerOptions {
static constexpr size_t PointerBufferSize = sf::hipc::DefaultServerManagerOptions::PointerBufferSize;
static constexpr size_t MaxDomains = sf::hipc::DefaultServerManagerOptions::MaxDomains;
static constexpr size_t MaxDomainObjects = sf::hipc::DefaultServerManagerOptions::MaxDomainObjects;
static constexpr bool CanDeferInvokeRequest = sf::hipc::DefaultServerManagerOptions::CanDeferInvokeRequest;
static constexpr bool CanManageMitmServers = true;
};
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
private:
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
};
ServerManager g_server_manager;
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
/* Acknowledge the mitm session. */
std::shared_ptr<::Service> fsrv;
sm::MitmProcessInfo client_info;
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
switch (port_index) {
case PortIndex_Mitm:
if (hos::GetVersion() < hos::Version_3_0_0) {
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<impl::IAmMitmInterface, NsAmMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
} else {
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<impl::IWebMitmInterface, NsWebMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
}
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}
void MitmModule::ThreadFunction(void *) {
/* Wait until initialization is complete. */
mitm::WaitInitialized();
/* Create mitm servers. */
if (hos::GetVersion() < hos::Version_3_0_0) {
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<NsAmMitmService>(PortIndex_Mitm, NsAmMitmServiceName)));
} else {
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<NsWebMitmService>(PortIndex_Mitm, NsWebMitmServiceName)));
}
/* Loop forever, servicing our services. */
g_server_manager.LoopProcess();
}
}

View File

@@ -1,24 +0,0 @@
/*
* 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>
#include "../amsmitm_module.hpp"
namespace ams::mitm::ns {
DEFINE_MITM_MODULE_CLASS(0x4000, AMS_GET_SYSTEM_THREAD_PRIORITY(ns, ApplicationManagerIpcSession) - 1);
}

View File

@@ -1,130 +0,0 @@
/*
* 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 "set_mitm_service.hpp"
#include "set_shim.h"
namespace ams::mitm::settings {
using namespace ams::settings;
namespace {
constinit os::ProcessId g_application_process_id = os::InvalidProcessId;
constinit cfg::OverrideLocale g_application_locale;
constinit bool g_valid_language;
constinit bool g_valid_region;
}
SetMitmService::SetMitmService(std::shared_ptr<::Service> &&s, const sm::MitmProcessInfo &c) : sf::MitmServiceImplBase(std::forward<std::shared_ptr<::Service>>(s), c) {
if (m_client_info.program_id == ncm::SystemProgramId::Ns) {
os::ProcessId application_process_id;
if (R_SUCCEEDED(ams::pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id))) && g_application_process_id == application_process_id) {
m_locale = g_application_locale;
m_is_valid_language = g_valid_language;
m_is_valid_region = g_valid_region;
m_got_locale = true;
} else {
this->InvalidateLocale();
}
} else {
this->InvalidateLocale();
}
}
Result SetMitmService::EnsureLocale() {
/* Optimization: if locale has already been gotten, we can stop. */
if (AMS_LIKELY(m_got_locale)) {
R_SUCCEED();
}
std::scoped_lock lk(m_lock);
const bool is_ns = m_client_info.program_id == ncm::SystemProgramId::Ns;
if (!m_got_locale) {
ncm::ProgramId program_id = m_client_info.program_id;
os::ProcessId application_process_id = os::InvalidProcessId;
if (is_ns) {
/* When NS asks for a locale, refresh to get the current application locale. */
R_TRY(ams::pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id)));
R_TRY(ams::pm::info::GetProgramId(std::addressof(program_id), application_process_id));
}
m_locale = cfg::GetOverrideLocale(program_id);
m_is_valid_language = settings::IsValidLanguageCode(m_locale.language_code);
m_is_valid_region = settings::IsValidRegionCode(m_locale.region_code);
m_got_locale = true;
if (is_ns) {
g_application_locale = m_locale;
g_valid_language = m_is_valid_language;
g_valid_region = m_is_valid_region;
g_application_process_id = application_process_id;
}
}
R_SUCCEED();
}
void SetMitmService::InvalidateLocale() {
std::scoped_lock lk(m_lock);
std::memset(std::addressof(m_locale), 0xCC, sizeof(m_locale));
m_is_valid_language = false;
m_is_valid_region = false;
m_got_locale = false;
}
Result SetMitmService::GetLanguageCode(sf::Out<settings::LanguageCode> out) {
this->EnsureLocale();
/* If there's no override locale, just use the actual one. */
if (AMS_UNLIKELY(!m_is_valid_language)) {
static_assert(sizeof(u64) == sizeof(settings::LanguageCode));
R_TRY(setGetLanguageCodeFwd(m_forward_service.get(), reinterpret_cast<u64 *>(std::addressof(m_locale.language_code))));
m_is_valid_language = true;
if (m_client_info.program_id == ncm::SystemProgramId::Ns) {
g_application_locale.language_code = m_locale.language_code;
g_valid_language = true;
}
}
out.SetValue(m_locale.language_code);
R_SUCCEED();
}
Result SetMitmService::GetRegionCode(sf::Out<settings::RegionCode> out) {
this->EnsureLocale();
/* If there's no override locale, just use the actual one. */
if (AMS_UNLIKELY(!m_is_valid_region)) {
static_assert(sizeof(::SetRegion) == sizeof(settings::RegionCode));
R_TRY(setGetRegionCodeFwd(m_forward_service.get(), reinterpret_cast<::SetRegion *>(std::addressof(m_locale.region_code))));
m_is_valid_region = true;
if (m_client_info.program_id == ncm::SystemProgramId::Ns) {
g_application_locale.region_code = m_locale.region_code;
g_valid_region = true;
}
}
out.SetValue(m_locale.region_code);
R_SUCCEED();
}
}

View File

@@ -1,53 +0,0 @@
/*
* 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>
#define AMS_SETTINGS_MITM_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, GetLanguageCode, (sf::Out<ams::settings::LanguageCode> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 4, Result, GetRegionCode, (sf::Out<ams::settings::RegionCode> out), (out))
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::settings, ISetMitmInterface, AMS_SETTINGS_MITM_INTERFACE_INFO, 0x7F7BAF0A)
namespace ams::mitm::settings {
class SetMitmService : public sf::MitmServiceImplBase {
private:
os::SdkMutex m_lock{};
cfg::OverrideLocale m_locale;
bool m_got_locale = false;
bool m_is_valid_language = false;
bool m_is_valid_region = false;
public:
SetMitmService(std::shared_ptr<::Service> &&s, const sm::MitmProcessInfo &c);
public:
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
/* We will mitm:
* - ns and games, to allow for overriding game locales.
*/
const bool is_game = (ncm::IsApplicationId(client_info.program_id) && !client_info.override_status.IsHbl());
return client_info.program_id == ncm::SystemProgramId::Ns || is_game;
}
private:
void InvalidateLocale();
Result EnsureLocale();
public:
Result GetLanguageCode(sf::Out<ams::settings::LanguageCode> out);
Result GetRegionCode(sf::Out<ams::settings::RegionCode> out);
};
static_assert(IsISetMitmInterface<SetMitmService>);
}

View File

@@ -1,36 +0,0 @@
/*
* 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 "set_shim.h"
static Result _setCmdNoInOut64(Service* srv, u64 *out, u32 cmd_id) {
return serviceDispatchOut(srv, cmd_id, *out);
}
static Result _setCmdNoInOutU32(Service* srv, u32 *out, u32 cmd_id) {
return serviceDispatchOut(srv, cmd_id, *out);
}
/* Forwarding shims. */
Result setGetLanguageCodeFwd(Service *s, u64* out) {
return _setCmdNoInOut64(s, out, 0);
}
Result setGetRegionCodeFwd(Service *s, SetRegion *out) {
s32 code=0;
Result rc = _setCmdNoInOutU32(s, (u32*)&code, 4);
if (R_SUCCEEDED(rc) && out) *out = code;
return rc;
}

View File

@@ -1,20 +0,0 @@
/**
* @file set_shim.h
* @brief Settings Services (fs) IPC wrapper for set.mitm.
* @author SciresM
* @copyright libnx Authors
*/
#pragma once
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Forwarding shims. */
Result setGetLanguageCodeFwd(Service *s, u64* out);
Result setGetRegionCodeFwd(Service *s, SetRegion *out);
#ifdef __cplusplus
}
#endif

View File

@@ -1,81 +0,0 @@
/*
* 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 "../amsmitm_initialization.hpp"
#include "setmitm_module.hpp"
#include "set_mitm_service.hpp"
#include "setsys_mitm_service.hpp"
namespace ams::mitm::settings {
namespace {
enum PortIndex {
PortIndex_SetMitm,
PortIndex_SetSysMitm,
PortIndex_Count,
};
constexpr sm::ServiceName SetMitmServiceName = sm::ServiceName::Encode("set");
constexpr sm::ServiceName SetSysMitmServiceName = sm::ServiceName::Encode("set:sys");
struct ServerOptions {
static constexpr size_t PointerBufferSize = 0x200;
static constexpr size_t MaxDomains = 0;
static constexpr size_t MaxDomainObjects = 0;
static constexpr bool CanDeferInvokeRequest = false;
static constexpr bool CanManageMitmServers = true;
};
constexpr size_t MaxSessions = 60;
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
private:
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
};
ServerManager g_server_manager;
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
/* Acknowledge the mitm session. */
std::shared_ptr<::Service> fsrv;
sm::MitmProcessInfo client_info;
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
switch (port_index) {
case PortIndex_SetMitm:
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<ISetMitmInterface, SetMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
case PortIndex_SetSysMitm:
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<ISetSysMitmInterface, SetSysMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}
void MitmModule::ThreadFunction(void *) {
/* Wait until initialization is complete. */
mitm::WaitInitialized();
/* Create mitm servers. */
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<SetMitmService>(PortIndex_SetMitm, SetMitmServiceName)));
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<SetSysMitmService>(PortIndex_SetSysMitm, SetSysMitmServiceName)));
/* Loop forever, servicing our services. */
g_server_manager.LoopProcess();
}
}

View File

@@ -1,24 +0,0 @@
/*
* 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>
#include "../amsmitm_module.hpp"
namespace ams::mitm::settings {
DEFINE_MITM_MODULE_CLASS(0x8000, AMS_GET_SYSTEM_THREAD_PRIORITY(settings, IpcServer) - 1);
}

View File

@@ -1,272 +0,0 @@
/*
* 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 "setsys_mitm_service.hpp"
#include "settings_sd_kvs.hpp"
#include "setsys_shim.h"
namespace ams::mitm::settings {
using namespace ams::settings;
namespace {
constexpr const char ExternalBluetoothDatabasePath[] = "@Sdcard:/atmosphere/bluetooth_devices.db";
constinit os::SdkMutex g_firmware_version_lock;
constinit bool g_cached_firmware_version;
constinit settings::FirmwareVersion g_firmware_version;
constinit settings::FirmwareVersion g_ams_firmware_version;
void CacheFirmwareVersion() {
if (AMS_LIKELY(g_cached_firmware_version)) {
return;
}
std::scoped_lock lk(g_firmware_version_lock);
if (AMS_UNLIKELY(g_cached_firmware_version)) {
return;
}
/* Mount firmware version data archive. */
{
R_ABORT_UNLESS(ams::fs::MountSystemData("sysver", ncm::SystemDataId::SystemVersion));
ON_SCOPE_EXIT { ams::fs::Unmount("sysver"); };
/* Firmware version file must exist. */
ams::fs::FileHandle file;
R_ABORT_UNLESS(ams::fs::OpenFile(std::addressof(file), "sysver:/file", fs::OpenMode_Read));
ON_SCOPE_EXIT { ams::fs::CloseFile(file); };
/* Must be possible to read firmware version from file. */
R_ABORT_UNLESS(ams::fs::ReadFile(file, 0, std::addressof(g_firmware_version), sizeof(g_firmware_version)));
g_ams_firmware_version = g_firmware_version;
}
/* Modify the atmosphere firmware version to display a custom version string. */
{
const auto api_info = exosphere::GetApiInfo();
const char emummc_char = emummc::IsActive() ? 'E' : 'S';
/* NOTE: We have carefully accounted for the size of the string we print. */
/* No truncation occurs assuming two-digits for all version number components. */
char display_version[sizeof(g_ams_firmware_version.display_version)];
util::SNPrintf(display_version, sizeof(display_version), "%s|AMS %u.%u.%u|%c", g_ams_firmware_version.display_version, api_info.GetMajorVersion(), api_info.GetMinorVersion(), api_info.GetMicroVersion(), emummc_char);
std::memcpy(g_ams_firmware_version.display_version, display_version, sizeof(display_version));
}
g_cached_firmware_version = true;
}
Result GetFirmwareVersionImpl(settings::FirmwareVersion *out, const sm::MitmProcessInfo &client_info) {
/* Ensure that we have the firmware version cached. */
CacheFirmwareVersion();
/* We want to give a special firmware version to the home menu title, and nothing else. */
/* This means qlaunch + maintenance menu, and nothing else. */
if (client_info.program_id == ncm::SystemAppletId::Qlaunch || client_info.program_id == ncm::SystemAppletId::MaintenanceMenu) {
*out = g_ams_firmware_version;
} else {
*out = g_firmware_version;
}
R_SUCCEED();
}
bool IsExternalBluetoothDatabaseEnabled() {
u8 en = 0;
settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_external_bluetooth_db");
return en;
}
bool HasExternalBluetoothDatabase() {
bool file_exists;
R_ABORT_UNLESS(fs::HasFile(std::addressof(file_exists), ExternalBluetoothDatabasePath));
return file_exists;
}
Result ReadExternalBluetoothDatabase(s32 *entries_read, settings::BluetoothDevicesSettings *db, size_t db_max_size) {
/* Open the external database file. */
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), ExternalBluetoothDatabasePath, fs::OpenMode_Read));
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Read the number of database entries stored in external database. */
u64 total_entries;
R_TRY(fs::ReadFile(file, 0, std::addressof(total_entries), sizeof(total_entries)));
u64 db_offset = sizeof(total_entries);
if (total_entries > db_max_size) {
/* Pairings are stored from least to most recent. Add offset to skip the older entries that won't fit. */
db_offset += (total_entries - db_max_size) * sizeof(settings::BluetoothDevicesSettings);
/* Cap number of database entries read to size of database on this firmware. */
total_entries = db_max_size;
}
/* Read database entries. */
R_TRY(fs::ReadFile(file, db_offset, db, total_entries * sizeof(settings::BluetoothDevicesSettings)));
/* Convert entries to the old format if running on a firmware below 13.0.0. */
if (hos::GetVersion() < hos::Version_13_0_0) {
for (size_t i = 0; i < total_entries; ++i) {
/* Copy the newer name field to the older one. */
util::TSNPrintf(db[i].name, sizeof(db[i].name), "%s", db[i].name2);
/* Clear the newer name field. */
std::memset(db[i].name2, 0, sizeof(db[i].name2));
}
}
/* Set the output. */
*entries_read = static_cast<s32>(total_entries);
R_SUCCEED();
}
Result StoreExternalBluetoothDatabase(const settings::BluetoothDevicesSettings *db, u64 total_entries) {
/* Open the external database file. */
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), ExternalBluetoothDatabasePath, fs::OpenMode_Write));
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Ensure the file is the appropriate size for the number of entries. */
R_TRY(fs::SetFileSize(file, sizeof(total_entries) + total_entries * sizeof(settings::BluetoothDevicesSettings)));
/* Write the number of database entries. */
R_TRY(fs::WriteFile(file, 0, std::addressof(total_entries), sizeof(total_entries), fs::WriteOption::None));
/* Write the database entries. */
u64 db_offset = sizeof(total_entries);
if (hos::GetVersion() < hos::Version_13_0_0) {
/* Convert entries to the new format if running on a firmware below 13.0.0. */
for (size_t i = 0; i < total_entries; ++i) {
/* Make a copy of the current database entry. */
settings::BluetoothDevicesSettings tmp = db[i];
/* Copy the older name field to the newer one. */
util::TSNPrintf(tmp.name2, sizeof(tmp.name2), "%s", tmp.name);
/* Clear the original name field. */
std::memset(tmp.name, 0, sizeof(tmp.name));
/* Write the converted database entry. */
R_TRY(fs::WriteFile(file, db_offset, std::addressof(tmp), sizeof(settings::BluetoothDevicesSettings), fs::WriteOption::None));
/* Advance to the next database entry. */
db_offset += sizeof(settings::BluetoothDevicesSettings);
}
/* Flush the data we've written. */
R_TRY(fs::FlushFile(file));
} else {
/* We can just write the database to the file. */
R_TRY(fs::WriteFile(file, db_offset, db, total_entries * sizeof(settings::BluetoothDevicesSettings), fs::WriteOption::Flush));
}
R_SUCCEED();
}
}
Result SetSysMitmService::GetFirmwareVersion(sf::Out<settings::FirmwareVersion> out) {
R_TRY(GetFirmwareVersionImpl(out.GetPointer(), m_client_info));
/* GetFirmwareVersion sanitizes the revision fields. */
out.GetPointer()->revision_major = 0;
out.GetPointer()->revision_minor = 0;
R_SUCCEED();
}
Result SetSysMitmService::GetFirmwareVersion2(sf::Out<settings::FirmwareVersion> out) {
R_RETURN(GetFirmwareVersionImpl(out.GetPointer(), m_client_info));
}
Result SetSysMitmService::SetBluetoothDevicesSettings(const sf::InMapAliasArray<settings::BluetoothDevicesSettings> &settings) {
/* We only want to perform additional logic when the external database setting is enabled. */
R_UNLESS(IsExternalBluetoothDatabaseEnabled(), sm::mitm::ResultShouldForwardToSession());
/* Create the external database if it doesn't exist. */
if (!HasExternalBluetoothDatabase()) {
R_TRY(fs::CreateFile(ExternalBluetoothDatabasePath, 0));
}
/* Backup the local database to the sd card. */
R_TRY(StoreExternalBluetoothDatabase(settings.GetPointer(), settings.GetSize()));
/* Ensure that the updated database is stored to the system save as usual. */
static_assert(sizeof(settings::BluetoothDevicesSettings) == sizeof(::SetSysBluetoothDevicesSettings));
R_TRY(setsysSetBluetoothDevicesSettingsFwd(m_forward_service.get(), reinterpret_cast<const ::SetSysBluetoothDevicesSettings *>(settings.GetPointer()), settings.GetSize()));
R_SUCCEED();
}
Result SetSysMitmService::GetBluetoothDevicesSettings(sf::Out<s32> out_count, const sf::OutMapAliasArray<settings::BluetoothDevicesSettings> &out) {
/* We only want to perform additional logic when the external database setting is enabled. */
R_UNLESS(IsExternalBluetoothDatabaseEnabled(), sm::mitm::ResultShouldForwardToSession());
if (!HasExternalBluetoothDatabase()) {
/* Fetch the local database from the system save. */
static_assert(sizeof(settings::BluetoothDevicesSettings) == sizeof(::SetSysBluetoothDevicesSettings));
R_TRY(setsysGetBluetoothDevicesSettingsFwd(m_forward_service.get(), out_count.GetPointer(), reinterpret_cast<::SetSysBluetoothDevicesSettings *>(out.GetPointer()), out.GetSize()));
/* Create the external database file. */
R_TRY(fs::CreateFile(ExternalBluetoothDatabasePath, 0));
/* Backup the local database to the sd card. */
R_TRY(StoreExternalBluetoothDatabase(out.GetPointer(), out_count.GetValue()));
} else {
/* Read the external database from the sd card. */
R_TRY(ReadExternalBluetoothDatabase(out_count.GetPointer(), out.GetPointer(), out.GetSize()));
}
R_SUCCEED();
}
Result SetSysMitmService::GetSettingsItemValueSize(sf::Out<u64> out_size, const settings::SettingsName &name, const settings::SettingsItemKey &key) {
R_TRY_CATCH(settings::fwdbg::GetSdCardKeyValueStoreSettingsItemValueSize(out_size.GetPointer(), name.value, key.value)) {
R_CATCH_RETHROW(sf::impl::ResultRequestContextChanged)
R_CONVERT_ALL(sm::mitm::ResultShouldForwardToSession());
} R_END_TRY_CATCH;
R_SUCCEED();
}
Result SetSysMitmService::GetSettingsItemValue(sf::Out<u64> out_size, const sf::OutBuffer &out, const settings::SettingsName &name, const settings::SettingsItemKey &key) {
R_TRY_CATCH(settings::fwdbg::GetSdCardKeyValueStoreSettingsItemValue(out_size.GetPointer(), out.GetPointer(), out.GetSize(), name.value, key.value)) {
R_CATCH_RETHROW(sf::impl::ResultRequestContextChanged)
R_CONVERT_ALL(sm::mitm::ResultShouldForwardToSession());
} R_END_TRY_CATCH;
R_SUCCEED();
}
Result SetSysMitmService::GetDebugModeFlag(sf::Out<bool> out) {
/* If we're not processing for am, just return the real flag value. */
R_UNLESS(m_client_info.program_id == ncm::SystemProgramId::Am, sm::mitm::ResultShouldForwardToSession());
/* Retrieve the user configuration. */
u8 en = 0;
settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_am_debug_mode");
out.SetValue(en != 0);
R_SUCCEED();
}
}

View File

@@ -1,54 +0,0 @@
/*
* 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>
#define AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 3, Result, GetFirmwareVersion, (sf::Out<ams::settings::FirmwareVersion> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 4, Result, GetFirmwareVersion2, (sf::Out<ams::settings::FirmwareVersion> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 11, Result, SetBluetoothDevicesSettings, (const sf::InMapAliasArray<ams::settings::BluetoothDevicesSettings> &settings), (settings)) \
AMS_SF_METHOD_INFO(C, H, 12, Result, GetBluetoothDevicesSettings, (sf::Out<s32> out_count, const sf::OutMapAliasArray<ams::settings::BluetoothDevicesSettings> &out), (out_count, out)) \
AMS_SF_METHOD_INFO(C, H, 37, Result, GetSettingsItemValueSize, (sf::Out<u64> out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, name, key)) \
AMS_SF_METHOD_INFO(C, H, 38, Result, GetSettingsItemValue, (sf::Out<u64> out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, out, name, key)) \
AMS_SF_METHOD_INFO(C, H, 62, Result, GetDebugModeFlag, (sf::Out<bool> out), (out))
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::settings, ISetSysMitmInterface, AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO, 0x0E82ED13)
namespace ams::mitm::settings {
class SetSysMitmService : public sf::MitmServiceImplBase {
public:
using MitmServiceImplBase::MitmServiceImplBase;
public:
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
/* We will mitm:
* - everything, because we want to intercept all settings requests.
*/
AMS_UNUSED(client_info);
return true;
}
public:
Result GetFirmwareVersion(sf::Out<ams::settings::FirmwareVersion> out);
Result GetFirmwareVersion2(sf::Out<ams::settings::FirmwareVersion> out);
Result SetBluetoothDevicesSettings(const sf::InMapAliasArray<ams::settings::BluetoothDevicesSettings> &settings);
Result GetBluetoothDevicesSettings(sf::Out<s32> out_count, const sf::OutMapAliasArray<ams::settings::BluetoothDevicesSettings> &out);
Result GetSettingsItemValueSize(sf::Out<u64> out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key);
Result GetSettingsItemValue(sf::Out<u64> out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key);
Result GetDebugModeFlag(sf::Out<bool> out);
};
static_assert(IsISetSysMitmInterface<SetSysMitmService>);
}

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