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,59 +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 "ams_bpc.os.horizon.h"
static Service g_amsBpcSrv;
NX_GENERATE_SERVICE_GUARD(amsBpc);
Result _amsBpcInitialize(void) {
Handle h;
Result rc = svcConnectToNamedPort(&h, "bpc:ams"); /* TODO: ams:bpc */
while (R_VALUE(rc) == KERNELRESULT(NotFound)) {
svcSleepThread(50000000ul);
rc = svcConnectToNamedPort(&h, "bpc:ams");
}
if (R_SUCCEEDED(rc)) serviceCreate(&g_amsBpcSrv, h);
return rc;
}
void _amsBpcCleanup(void) {
serviceClose(&g_amsBpcSrv);
}
Service *amsBpcGetServiceSession(void) {
return &g_amsBpcSrv;
}
Result amsBpcRebootToFatalError(void *ctx) {
/* Note: this takes in an sts::ams::FatalErrorContext. */
/* static_assert(sizeof() == 0x450) is done at type definition. */
return serviceDispatch(&g_amsBpcSrv, 65000,
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias | SfBufferAttr_FixedSize },
.buffers = { { ctx, 0x450 } },
);
}
Result amsBpcSetRebootPayload(const void *src, size_t src_size) {
return serviceDispatch(&g_amsBpcSrv, 65001,
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias },
.buffers = { { src, src_size } },
);
}

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/>.
*/
#pragma once
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
Result amsBpcInitialize(void);
void amsBpcExit(void);
Service *amsBpcGetServiceSession(void);
Result amsBpcRebootToFatalError(void *ctx);
Result amsBpcSetRebootPayload(const void *src, size_t src_size);
#ifdef __cplusplus
}
#endif

View File

@@ -1,145 +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::emummc {
namespace {
/* Convenience Definitions. */
constexpr u32 StorageMagic = util::FourCC<'E','F','S','0'>::Code;
constexpr size_t MaxDirLen = 0x7F;
/* Types. */
struct BaseConfig {
u32 magic;
u32 type;
u32 id;
u32 fs_version;
};
struct PartitionConfig {
u64 start_sector;
};
struct FileConfig {
char path[MaxDirLen + 1];
};
struct ExosphereConfig {
BaseConfig base_cfg;
union {
PartitionConfig partition_cfg;
FileConfig file_cfg;
};
char emu_dir_path[MaxDirLen + 1];
};
enum Storage : u32 {
Storage_Emmc,
Storage_Sd,
Storage_SdFile,
Storage_Count,
};
/* Globals. */
constinit os::SdkMutex g_lock;
constinit ExosphereConfig g_exo_config = {};
constinit bool g_is_emummc;
constinit bool g_has_cached;
/* Helpers. */
void CacheValues() {
std::scoped_lock lk(g_lock);
if (g_has_cached) {
return;
}
/* Retrieve and cache values. */
{
alignas(os::MemoryPageSize) std::byte path_storage[2 * (MaxDirLen + 1)];
struct {
char file_path[MaxDirLen + 1];
char nintendo_path[MaxDirLen + 1];
} *paths = reinterpret_cast<decltype(paths)>(std::addressof(path_storage));
/* Retrieve paths from secure monitor. */
AMS_ABORT_UNLESS(spl::smc::AtmosphereGetEmummcConfig(std::addressof(g_exo_config), paths, 0) == spl::smc::Result::Success);
const Storage storage = static_cast<Storage>(g_exo_config.base_cfg.type);
g_is_emummc = g_exo_config.base_cfg.magic == StorageMagic && storage != Storage_Emmc;
/* Format paths. */
{
char tmp_path[MaxDirLen + 1];
/* Format paths. */
if (storage == Storage_SdFile) {
util::TSNPrintf(tmp_path, sizeof(tmp_path), "/%s", paths->file_path);
R_ABORT_UNLESS(fs::PathFormatter::Normalize(g_exo_config.file_cfg.path, sizeof(g_exo_config.file_cfg.path), tmp_path, std::strlen(tmp_path) + 1, fs::PathFlags{}));
}
util::TSNPrintf(tmp_path, sizeof(tmp_path), "/%s", paths->nintendo_path);
R_ABORT_UNLESS(fs::PathFormatter::Normalize(g_exo_config.emu_dir_path, sizeof(g_exo_config.emu_dir_path), tmp_path, std::strlen(tmp_path) + 1, fs::PathFlags{}));
/* If we're emummc, implement default nintendo redirection path. */
if (g_is_emummc && std::strcmp(g_exo_config.emu_dir_path, "/") == 0) {
util::TSNPrintf(tmp_path, sizeof(tmp_path), "/emummc/Nintendo_%04x", g_exo_config.base_cfg.id);
R_ABORT_UNLESS(fs::PathFormatter::Normalize(g_exo_config.emu_dir_path, sizeof(g_exo_config.emu_dir_path), tmp_path, std::strlen(tmp_path) + 1, fs::PathFlags{}));
}
}
}
g_has_cached = true;
}
}
/* Get whether emummc is active. */
bool IsActive() {
CacheValues();
return g_is_emummc;
}
/* Get the active emummc id. */
u32 GetActiveId() {
CacheValues();
return g_exo_config.base_cfg.id;
}
/* Get Nintendo redirection path. */
const char *GetNintendoDirPath() {
CacheValues();
if (!g_is_emummc) {
return nullptr;
}
return g_exo_config.emu_dir_path;
}
/* Get Emummc folderpath, NULL if not file-based. */
const char *GetFilePath() {
CacheValues();
if (!g_is_emummc || g_exo_config.base_cfg.type != Storage_SdFile) {
return nullptr;
}
return g_exo_config.file_cfg.path;
}
}

View File

@@ -1,181 +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 "ams_bpc.os.horizon.h"
namespace ams {
namespace {
#if defined(ATMOSPHERE_ARCH_ARM64)
inline u64 GetPc() {
u64 pc;
__asm__ __volatile__ ("adr %[pc], ." : [pc]"=&r"(pc) :: );
return pc;
}
struct StackFrame {
u64 fp;
u64 lr;
};
#else
#error "Unknown architecture for os Horizon"
#endif
}
void InitializeForBoot() {
R_ABORT_UNLESS(amsBpcInitialize());
}
void SetInitialRebootPayload(const void *src, size_t src_size) {
R_ABORT_UNLESS(amsBpcSetRebootPayload(src, src_size));
}
void WEAK_SYMBOL ExceptionHandler(FatalErrorContext *ctx) {
R_ABORT_UNLESS(amsBpcInitialize());
R_ABORT_UNLESS(amsBpcRebootToFatalError(ctx));
while (1) { /* ... */ }
}
void CrashHandler(ThreadExceptionDump *ctx) {
FatalErrorContext ams_ctx;
/* Convert thread dump to atmosphere dump. */
{
ams_ctx.magic = FatalErrorContext::Magic;
ams_ctx.error_desc = ctx->error_desc;
ams_ctx.program_id = os::GetCurrentProgramId().value;
for (size_t i = 0; i < FatalErrorContext::NumGprs; i++) {
ams_ctx.gprs[i] = ctx->cpu_gprs[i].x;
}
if (ams_ctx.error_desc == FatalErrorContext::DataAbortErrorDesc &&
ams_ctx.gprs[27] == FatalErrorContext::StdAbortMagicAddress &&
ams_ctx.gprs[28] == FatalErrorContext::StdAbortMagicValue)
{
/* Detect std::abort(). */
ams_ctx.error_desc = FatalErrorContext::StdAbortErrorDesc;
}
ams_ctx.fp = ctx->fp.x;
ams_ctx.lr = ctx->lr.x;
ams_ctx.sp = ctx->sp.x;
ams_ctx.pc = ctx->pc.x;
ams_ctx.pstate = ctx->pstate;
ams_ctx.afsr0 = static_cast<u32>(::ams::exosphere::GetVersion(ATMOSPHERE_RELEASE_VERSION));
ams_ctx.afsr1 = static_cast<u32>(hos::GetVersion());
ams_ctx.far = ctx->far.x;
ams_ctx.report_identifier = armGetSystemTick();
/* Detect stack overflow. */
if (ams_ctx.error_desc == FatalErrorContext::DataAbortErrorDesc) {
svc::lp64::MemoryInfo mem_info;
svc::PageInfo page_info;
if (/* Check if stack pointer is in guard page. */
R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), ams_ctx.sp)) &&
mem_info.state == svc::MemoryState_Free &&
/* Check if stack pointer fell off stack. */
R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), ams_ctx.sp + 0x1000)) &&
mem_info.state == svc::MemoryState_Stack) {
ams_ctx.error_desc = FatalErrorContext::StackOverflowErrorDesc;
}
}
/* Grab module base. */
{
svc::lp64::MemoryInfo mem_info;
svc::PageInfo page_info;
if (R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), GetPc()))) {
ams_ctx.module_base = mem_info.base_address;
} else {
ams_ctx.module_base = 0;
}
}
ams_ctx.stack_trace_size = 0;
u64 cur_fp = ams_ctx.fp;
for (size_t i = 0; i < FatalErrorContext::MaxStackTrace; i++) {
/* Validate current frame. */
if (cur_fp == 0 || (cur_fp & 0xF)) {
break;
}
/* Read a new frame. */
StackFrame cur_frame;
svc::lp64::MemoryInfo mem_info;
svc::PageInfo page_info;
if (R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), cur_fp)) && (mem_info.permission & svc::MemoryPermission_Read) == svc::MemoryPermission_Read) {
std::memcpy(std::addressof(cur_frame), reinterpret_cast<void *>(cur_fp), sizeof(cur_frame));
} else {
break;
}
/* Advance to the next frame. */
ams_ctx.stack_trace[ams_ctx.stack_trace_size++] = cur_frame.lr;
cur_fp = cur_frame.fp;
}
/* Clear unused parts of stack trace. */
for (size_t i = ams_ctx.stack_trace_size; i < FatalErrorContext::MaxStackTrace; i++) {
ams_ctx.stack_trace[i] = 0;
}
/* Grab up to 0x100 of stack. */
{
svc::lp64::MemoryInfo mem_info;
svc::PageInfo page_info;
if (R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), ams_ctx.sp)) && (mem_info.permission & svc::MemoryPermission_Read) == svc::MemoryPermission_Read) {
size_t copy_size = std::min(FatalErrorContext::MaxStackDumpSize, static_cast<size_t>(mem_info.base_address + mem_info.size - ams_ctx.sp));
ams_ctx.stack_dump_size = copy_size;
std::memcpy(ams_ctx.stack_dump, reinterpret_cast<void *>(ams_ctx.sp), copy_size);
} else {
ams_ctx.stack_dump_size = 0;
}
}
/* Grab 0x100 of tls. */
std::memcpy(ams_ctx.tls, svc::GetThreadLocalRegion(), sizeof(ams_ctx.tls));
}
/* Just call the user exception handler. */
::ams::ExceptionHandler(std::addressof(ams_ctx));
}
NORETURN void AbortImpl();
}
extern "C" {
/* Redefine C++ exception handlers. Requires wrap linker flag. */
#define WRAP_ABORT_FUNC(func) void NORETURN __wrap_##func(void) { ::ams::AbortImpl(); __builtin_unreachable(); }
WRAP_ABORT_FUNC(__cxa_throw)
WRAP_ABORT_FUNC(__cxa_rethrow)
WRAP_ABORT_FUNC(__cxa_allocate_exception)
WRAP_ABORT_FUNC(__cxa_free_exception)
WRAP_ABORT_FUNC(__cxa_begin_catch)
WRAP_ABORT_FUNC(__cxa_end_catch)
WRAP_ABORT_FUNC(__cxa_call_unexpected)
WRAP_ABORT_FUNC(__cxa_call_terminate)
WRAP_ABORT_FUNC(__gxx_personality_v0)
WRAP_ABORT_FUNC(_ZSt19__throw_logic_errorPKc)
WRAP_ABORT_FUNC(_ZSt20__throw_length_errorPKc)
WRAP_ABORT_FUNC(_ZNSt11logic_errorC2EPKc)
/* TODO: We may wish to consider intentionally not defining an _Unwind_Resume wrapper. */
/* This would mean that a failure to wrap all exception functions is a linker error. */
WRAP_ABORT_FUNC(_Unwind_Resume)
#undef WRAP_ABORT_FUNC
}

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>
extern "C" void NORETURN __real_exit(int rc);
namespace ams {
WEAK_SYMBOL void *Malloc(size_t size) {
return std::malloc(size);
}
WEAK_SYMBOL void Free(void *ptr) {
return std::free(ptr);
}
WEAK_SYMBOL void *MallocForRapidJson(size_t size) {
return std::malloc(size);
}
WEAK_SYMBOL void *ReallocForRapidJson(void *ptr, size_t size) {
return std::realloc(ptr, size);
}
WEAK_SYMBOL void FreeForRapidJson(void *ptr) {
return std::free(ptr);
}
WEAK_SYMBOL void NORETURN Exit(int rc) {
__real_exit(rc);
__builtin_unreachable();
}
NOINLINE NORETURN void AbortImpl() {
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
/* Just perform a data abort. */
register u64 addr __asm__("x27") = FatalErrorContext::StdAbortMagicAddress;
register u64 val __asm__("x28") = FatalErrorContext::StdAbortMagicValue;
while (true) {
__asm__ __volatile__ (
"str %[val], [%[addr]]"
:
: [val]"r"(val), [addr]"r"(addr)
);
}
#else
/* What should be done here? */
AMS_INFINITE_LOOP();
#endif
__builtin_unreachable();
}
}
extern "C" {
/* Redefine abort to trigger these handlers. */
void abort();
/* Redefine C++ exception handlers. Requires wrap linker flag. */
#define WRAP_ABORT_FUNC(func) void NORETURN __wrap_##func(void) { ::ams::AbortImpl(); __builtin_unreachable(); }
WRAP_ABORT_FUNC(__cxa_pure_virtual)
#undef WRAP_ABORT_FUNC
void NORETURN __wrap_exit(int rc) { ::ams::Exit(rc); __builtin_unreachable(); }
}
/* Custom abort handler, so that std::abort will trigger these. */
void abort() {
ams::AbortImpl();
__builtin_unreachable();
}

View File

@@ -1,59 +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 os {
void Initialize();
}
void *Malloc(size_t size) {
return std::malloc(size);
}
void Free(void *ptr) {
return std::free(ptr);
}
void *MallocForRapidJson(size_t size) {
return std::malloc(size);
}
void *ReallocForRapidJson(void *ptr, size_t size) {
return std::realloc(ptr, size);
}
void FreeForRapidJson(void *ptr) {
return std::free(ptr);
}
NORETURN void AbortImpl() {
std::abort();
}
}
extern "C" {
/* Redefine C++ exception handlers. Requires wrap linker flag. */
#define WRAP_ABORT_FUNC(func) void NORETURN __wrap_##func(void) { std::abort(); __builtin_unreachable(); }
WRAP_ABORT_FUNC(__cxa_pure_virtual)
#undef WRAP_ABORT_FUNC
}

View File

@@ -1,59 +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 os {
void Initialize();
}
void *Malloc(size_t size) {
return std::malloc(size);
}
void Free(void *ptr) {
return std::free(ptr);
}
void *MallocForRapidJson(size_t size) {
return std::malloc(size);
}
void *ReallocForRapidJson(void *ptr, size_t size) {
return std::realloc(ptr, size);
}
void FreeForRapidJson(void *ptr) {
return std::free(ptr);
}
NORETURN void AbortImpl() {
std::abort();
}
}
extern "C" {
/* Redefine C++ exception handlers. Requires wrap linker flag. */
#define WRAP_ABORT_FUNC(func) void NORETURN __wrap_##func(void) { std::abort(); __builtin_unreachable(); }
WRAP_ABORT_FUNC(__cxa_pure_virtual)
#undef WRAP_ABORT_FUNC
}

View File

@@ -1,76 +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>
extern "C" char **__real___p__acmdln(void);
extern "C" _invalid_parameter_handler __real__set_invalid_parameter_handler(_invalid_parameter_handler);
namespace ams {
namespace os {
void Initialize();
}
void *Malloc(size_t size) {
return std::malloc(size);
}
void Free(void *ptr) {
return std::free(ptr);
}
void *MallocForRapidJson(size_t size) {
return std::malloc(size);
}
void *ReallocForRapidJson(void *ptr, size_t size) {
return std::realloc(ptr, size);
}
void FreeForRapidJson(void *ptr) {
return std::free(ptr);
}
NORETURN void AbortImpl() {
std::abort();
}
}
extern "C" {
/* Redefine C++ exception handlers. Requires wrap linker flag. */
#define WRAP_ABORT_FUNC(func) void NORETURN __wrap_##func(void) { std::abort(); __builtin_unreachable(); }
WRAP_ABORT_FUNC(__cxa_pure_virtual)
#undef WRAP_ABORT_FUNC
/* On windows, mingw may attempt to call malloc before we've initialized globals to set up the command line. */
/* We perform some critical init here, to make that work. */
char **__wrap___p__acmdln(void) {
::ams::os::Initialize();
return __real___p__acmdln();
}
/* On some mingw gcc versions, acmdln isn't used, so we need to hook a different part of crt init. */
_invalid_parameter_handler __wrap__set_invalid_parameter_handler(_invalid_parameter_handler handler) {
::ams::os::Initialize();
return __real__set_invalid_parameter_handler(handler);
}
}

View File

@@ -1,91 +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::exosphere {
ApiInfo GetApiInfo() {
u64 exosphere_cfg;
if (R_FAILED(spl::impl::GetConfig(std::addressof(exosphere_cfg), spl::ConfigItem::ExosphereApiVersion))) {
R_ABORT_UNLESS(exosphere::ResultNotPresent());
}
return ApiInfo{ util::BitPack64{exosphere_cfg} };
}
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
void ForceRebootToRcm() {
R_ABORT_UNLESS(spl::impl::SetConfig(spl::ConfigItem::ExosphereNeedsReboot, 1));
}
void ForceRebootToIramPayload() {
R_ABORT_UNLESS(spl::impl::SetConfig(spl::ConfigItem::ExosphereNeedsReboot, 2));
}
void ForceRebootToFatalError() {
R_ABORT_UNLESS(spl::impl::SetConfig(spl::ConfigItem::ExosphereNeedsReboot, 3));
}
void ForceRebootByPmic() {
R_ABORT_UNLESS(spl::impl::SetConfig(spl::ConfigItem::ExosphereNeedsReboot, 4));
}
void ForceShutdown() {
R_ABORT_UNLESS(spl::impl::SetConfig(spl::ConfigItem::ExosphereNeedsShutdown, 1));
}
void CopyToIram(uintptr_t iram_dst, const void *dram_src, size_t size) {
spl::smc::AtmosphereCopyToIram(iram_dst, dram_src, size);
}
void CopyFromIram(void *dram_dst, uintptr_t iram_src, size_t size) {
spl::smc::AtmosphereCopyFromIram(dram_dst, iram_src, size);
}
namespace {
inline u64 GetU64ConfigItem(spl::ConfigItem cfg) {
u64 tmp;
R_ABORT_UNLESS(spl::smc::ConvertResult(spl::smc::GetConfig(std::addressof(tmp), 1, cfg)));
return tmp;
}
inline bool GetBooleanConfigItem(spl::ConfigItem cfg) {
return GetU64ConfigItem(cfg) != 0;
}
}
bool IsRcmBugPatched() {
return spl::impl::GetConfigBool(spl::ConfigItem::ExosphereHasRcmBugPatch);
}
bool ShouldBlankProdInfo() {
return spl::impl::GetConfigBool(spl::ConfigItem::ExosphereBlankProdInfo);
}
bool ShouldAllowWritesToProdInfo() {
return spl::impl::GetConfigBool(spl::ConfigItem::ExosphereAllowCalWrites);
}
u64 GetDeviceId() {
u64 device_id;
R_ABORT_UNLESS(spl::impl::GetConfig(std::addressof(device_id), spl::ConfigItem::DeviceId));
return device_id;
}
#endif
}

View File

@@ -1,460 +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::boot2 {
namespace {
/* Launch lists. */
/* psc, bus, pcv is the minimal set of required programs to get SD card. */
/* bus depends on pcie, and pcv depends on settings. */
constexpr const ncm::SystemProgramId PreSdCardLaunchPrograms[] = {
ncm::SystemProgramId::Psc, /* psc */
ncm::SystemProgramId::Pcie, /* pcie */
ncm::SystemProgramId::Bus, /* bus */
ncm::SystemProgramId::Settings, /* settings */
ncm::SystemProgramId::Pcv, /* pcv */
ncm::SystemProgramId::Usb, /* usb */
};
constexpr size_t NumPreSdCardLaunchPrograms = util::size(PreSdCardLaunchPrograms);
constexpr const ncm::SystemProgramId AdditionalLaunchPrograms[] = {
ncm::SystemProgramId::Omm, /* omm */
ncm::SystemProgramId::Am, /* am */
ncm::SystemProgramId::NvServices, /* nvservices */
ncm::SystemProgramId::NvnFlinger, /* nvnflinger */
ncm::SystemProgramId::Vi, /* vi */
ncm::SystemProgramId::Pgl, /* pgl */
ncm::SystemProgramId::Ns, /* ns */
//ncm::SystemProgramId::LogManager, /* lm */
ncm::SystemProgramId::Ppc, /* ppc */
ncm::SystemProgramId::Ptm, /* ptm */
ncm::SystemProgramId::Hid, /* hid */
ncm::SystemProgramId::Audio, /* audio */
ncm::SystemProgramId::Lbl, /* lbl */
ncm::SystemProgramId::Wlan, /* wlan */
ncm::SystemProgramId::Bluetooth, /* bluetooth */
ncm::SystemProgramId::BsdSockets, /* bsdsockets */
ncm::SystemProgramId::Eth, /* eth */
ncm::SystemProgramId::Nifm, /* nifm */
ncm::SystemProgramId::Ldn, /* ldn */
ncm::SystemProgramId::Account, /* account */
ncm::SystemProgramId::Friends, /* friends */
ncm::SystemProgramId::Nfc, /* nfc */
ncm::SystemProgramId::JpegDec, /* jpegdec */
ncm::SystemProgramId::CapSrv, /* capsrv */
ncm::SystemProgramId::Ssl, /* ssl */
ncm::SystemProgramId::Nim, /* nim */
ncm::SystemProgramId::Bcat, /* bcat */
ncm::SystemProgramId::Erpt, /* erpt */
ncm::SystemProgramId::Es, /* es */
ncm::SystemProgramId::Pctl, /* pctl */
ncm::SystemProgramId::Btm, /* btm */
ncm::SystemProgramId::Eupld, /* eupld */
ncm::SystemProgramId::Glue, /* glue */
/* ncm::SystemProgramId::Eclct, */ /* eclct */ /* Skip launching error collection in Atmosphere to lessen telemetry. */
ncm::SystemProgramId::Npns, /* npns */
ncm::SystemProgramId::Fatal, /* fatal */
ncm::SystemProgramId::Ro, /* ro */
ncm::SystemProgramId::Profiler, /* profiler */
ncm::SystemProgramId::Sdb, /* sdb */
ncm::SystemProgramId::Olsc, /* olsc */
ncm::SystemProgramId::Ngc, /* ngc */
ncm::SystemProgramId::Ngct, /* ngct */
};
constexpr size_t NumAdditionalLaunchPrograms = util::size(AdditionalLaunchPrograms);
constexpr const ncm::SystemProgramId AdditionalMaintenanceLaunchPrograms[] = {
ncm::SystemProgramId::Omm, /* omm */
ncm::SystemProgramId::Am, /* am */
ncm::SystemProgramId::NvServices, /* nvservices */
ncm::SystemProgramId::NvnFlinger, /* nvnflinger */
ncm::SystemProgramId::Vi, /* vi */
ncm::SystemProgramId::Pgl, /* pgl */
ncm::SystemProgramId::Ns, /* ns */
//ncm::SystemProgramId::LogManager, /* lm */
ncm::SystemProgramId::Ppc, /* ppc */
ncm::SystemProgramId::Ptm, /* ptm */
ncm::SystemProgramId::Hid, /* hid */
ncm::SystemProgramId::Audio, /* audio */
ncm::SystemProgramId::Lbl, /* lbl */
ncm::SystemProgramId::Wlan, /* wlan */
ncm::SystemProgramId::Bluetooth, /* bluetooth */
ncm::SystemProgramId::BsdSockets, /* bsdsockets */
ncm::SystemProgramId::Eth, /* eth */
ncm::SystemProgramId::Nifm, /* nifm */
ncm::SystemProgramId::Ldn, /* ldn */
ncm::SystemProgramId::Account, /* account */
ncm::SystemProgramId::Nfc, /* nfc */
ncm::SystemProgramId::JpegDec, /* jpegdec */
ncm::SystemProgramId::CapSrv, /* capsrv */
ncm::SystemProgramId::Ssl, /* ssl */
ncm::SystemProgramId::Nim, /* nim */
ncm::SystemProgramId::Erpt, /* erpt */
ncm::SystemProgramId::Es, /* es */
ncm::SystemProgramId::Pctl, /* pctl */
ncm::SystemProgramId::Btm, /* btm */
ncm::SystemProgramId::Glue, /* glue */
/* ncm::SystemProgramId::Eclct, */ /* eclct */ /* Skip launching error collection in Atmosphere to lessen telemetry. */
ncm::SystemProgramId::Fatal, /* fatal */
ncm::SystemProgramId::Ro, /* ro */
ncm::SystemProgramId::Profiler, /* profiler */
ncm::SystemProgramId::Sdb, /* sdb */
ncm::SystemProgramId::Olsc, /* olsc */
ncm::SystemProgramId::Ngc, /* ngc */
ncm::SystemProgramId::Ngct, /* ngct */
};
constexpr size_t NumAdditionalMaintenanceLaunchPrograms = util::size(AdditionalMaintenanceLaunchPrograms);
/* Helpers. */
inline bool IsHexadecimal(const char *str) {
while (*str) {
if (!std::isxdigit(static_cast<unsigned char>(*(str++)))) {
return false;
}
}
return true;
}
inline bool IsNewLine(char c) {
return c == '\r' || c == '\n';
}
inline bool IsAllowedLaunchProgram(const ncm::ProgramLocation &loc) {
if (loc.program_id == ncm::SystemProgramId::Pgl) {
return hos::GetVersion() >= hos::Version_10_0_0;
}
return true;
}
void LaunchProgram(os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 launch_flags) {
os::ProcessId process_id = os::InvalidProcessId;
/* Only launch the process if we're allowed to. */
if (IsAllowedLaunchProgram(loc)) {
/* Launch, lightly validate result. */
{
const auto launch_result = pm::shell::LaunchProgram(std::addressof(process_id), loc, launch_flags);
AMS_ABORT_UNLESS(!(svc::ResultOutOfResource::Includes(launch_result)));
AMS_ABORT_UNLESS(!(svc::ResultOutOfMemory::Includes(launch_result)));
AMS_ABORT_UNLESS(!(svc::ResultLimitReached::Includes(launch_result)));
}
if (out_process_id) {
*out_process_id = process_id;
}
}
}
void LaunchList(const ncm::SystemProgramId *launch_list, size_t num_entries) {
for (size_t i = 0; i < num_entries; i++) {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(launch_list[i], ncm::StorageId::BuiltInSystem), 0);
}
}
bool GetGpioPadLow(DeviceCode device_code) {
gpio::GpioPadSession button{};
if (R_FAILED(gpio::OpenSession(std::addressof(button), device_code))) {
return false;
}
/* Ensure we close even on early return. */
ON_SCOPE_EXIT { gpio::CloseSession(std::addressof(button)); };
/* Set direction input. */
gpio::SetDirection(std::addressof(button), gpio::Direction_Input);
return gpio::GetValue(std::addressof(button)) == gpio::GpioValue_Low;
}
bool IsForceMaintenance() {
u8 force_maintenance = 1;
settings::fwdbg::GetSettingsItemValue(std::addressof(force_maintenance), sizeof(force_maintenance), "boot", "force_maintenance");
return force_maintenance != 0;
}
bool IsHtcEnabled() {
u8 enable_htc = 0;
settings::fwdbg::GetSettingsItemValue(std::addressof(enable_htc), sizeof(enable_htc), "atmosphere", "enable_htc");
return enable_htc != 0;
}
bool IsStandaloneGdbstubEnabled() {
u8 enable_gdbstub = 0;
settings::fwdbg::GetSettingsItemValue(std::addressof(enable_gdbstub), sizeof(enable_gdbstub), "atmosphere", "enable_standalone_gdbstub");
return enable_gdbstub != 0;
}
bool IsAtmosphereLogManagerEnabled() {
/* If htc is enabled, ams log manager is enabled. */
if (IsHtcEnabled()) {
return true;
}
u8 enable_ams_lm = 0;
settings::fwdbg::GetSettingsItemValue(std::addressof(enable_ams_lm), sizeof(enable_ams_lm), "atmosphere", "enable_log_manager");
return enable_ams_lm != 0;
}
bool IsMaintenanceMode() {
/* Contact set:sys, retrieve boot!force_maintenance. */
if (IsForceMaintenance()) {
return true;
}
/* Contact GPIO, read plus/minus buttons. */
{
return GetGpioPadLow(gpio::DeviceCode_ButtonVolUp) && GetGpioPadLow(gpio::DeviceCode_ButtonVolDn);
}
}
template<typename F>
void IterateOverFlaggedProgramsOnSdCard(F f) {
/* Validate that the contents directory exists. */
fs::DirectoryHandle contents_dir;
if (R_FAILED(fs::OpenDirectory(std::addressof(contents_dir), "sdmc:/atmosphere/contents", fs::OpenDirectoryMode_Directory))) {
return;
}
ON_SCOPE_EXIT { fs::CloseDirectory(contents_dir); };
/* Iterate over entries in the contents directory */
fs::DirectoryEntry entry;
s64 count;
while (R_SUCCEEDED(fs::ReadDirectory(std::addressof(count), std::addressof(entry), contents_dir, 1)) && count == 1) {
/* Check that the subdirectory can be converted to a program id. */
if (std::strlen(entry.name) == 2 * sizeof(ncm::ProgramId) && IsHexadecimal(entry.name)) {
/* Check if we've already launched the program. */
ncm::ProgramId program_id{std::strtoul(entry.name, nullptr, 16)};
/* Check if the program is flagged. */
if (!cfg::HasContentSpecificFlag(program_id, "boot2")) {
continue;
}
/* Call the iteration callback. */
f(program_id);
}
}
}
void DetectAndDeclareFutureMitms() {
IterateOverFlaggedProgramsOnSdCard([](ncm::ProgramId program_id) {
/* When we find a flagged program, check if it has a mitm list. */
char mitm_list[0x400];
size_t mitm_list_size = 0;
/* Read the mitm list off the SD card. */
{
char path[fs::EntryNameLengthMax];
util::SNPrintf(path, sizeof(path), "sdmc:/atmosphere/contents/%016" PRIx64 "/mitm.lst", static_cast<u64>(program_id));
fs::FileHandle f;
if (R_FAILED(fs::OpenFile(std::addressof(f), path, fs::OpenMode_Read))) {
return;
}
ON_SCOPE_EXIT { fs::CloseFile(f); };
R_ABORT_UNLESS(fs::ReadFile(std::addressof(mitm_list_size), f, 0, mitm_list, sizeof(mitm_list)));
}
/* Validate read size. */
if (mitm_list_size > sizeof(mitm_list) || mitm_list_size == 0) {
return;
}
/* Iterate over the contents of the file. */
/* We expect one service name per non-empty line, 1-8 characters. */
size_t offset = 0;
while (offset < mitm_list_size) {
/* Continue to the start of the next name. */
while (IsNewLine(mitm_list[offset])) {
offset++;
}
if (offset >= mitm_list_size) {
break;
}
/* Get the length of the current name. */
size_t name_len;
for (name_len = 0; name_len <= sizeof(sm::ServiceName) && offset + name_len < mitm_list_size; name_len++) {
if (IsNewLine(mitm_list[offset + name_len])) {
break;
}
}
/* Allow empty lines. */
if (name_len == 0) {
continue;
}
/* Don't allow invalid lines. */
AMS_ABORT_UNLESS(name_len <= sizeof(sm::ServiceName));
/* Declare the service. */
R_ABORT_UNLESS(sm::mitm::DeclareFutureMitm(sm::ServiceName::Encode(mitm_list + offset, name_len)));
/* Advance to the next line. */
offset += name_len;
}
});
}
void LaunchFlaggedProgramsOnSdCard() {
IterateOverFlaggedProgramsOnSdCard([](ncm::ProgramId program_id) {
/* Check if we've already launched the program. */
if (pm::info::HasLaunchedBootProgram(program_id)) {
return;
}
/* Launch the program. */
LaunchProgram(nullptr, ncm::ProgramLocation::Make(program_id, ncm::StorageId::None), 0);
});
}
bool IsUsbRequiredToMountSdCard() {
return hos::GetVersion() >= hos::Version_9_0_0;
}
}
/* Boot2 API. */
void LaunchPreSdCardBootProgramsAndBoot2() {
/* This code is normally run by PM. */
/* Wait until fs.mitm has installed itself. We want this to happen as early as possible. */
R_ABORT_UNLESS(sm::mitm::WaitMitm(sm::ServiceName::Encode("fsp-srv")));
/* Launch programs required to mount the SD card. */
/* psc, bus, pcv (and usb on newer firmwares) is the minimal set of required programs. */
/* bus depends on pcie, and pcv depends on settings. */
{
/* Launch psc. */
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Psc, ncm::StorageId::BuiltInSystem), 0);
/* Launch pcie. */
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Pcie, ncm::StorageId::BuiltInSystem), 0);
/* Launch bus. */
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Bus, ncm::StorageId::BuiltInSystem), 0);
/* Launch settings. */
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Settings, ncm::StorageId::BuiltInSystem), 0);
/* NOTE: Here we work around a race condition in the boot process by ensuring that settings initializes its db. */
{
/* Connect to set:sys. */
R_ABORT_UNLESS(::setsysInitialize());
ON_SCOPE_EXIT { ::setsysExit(); };
/* Retrieve setting from the database. */
u8 force_maintenance = 0;
settings::fwdbg::GetSettingsItemValue(std::addressof(force_maintenance), sizeof(force_maintenance), "boot", "force_maintenance");
}
/* Launch pcv. */
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Pcv, ncm::StorageId::BuiltInSystem), 0);
/* On 9.0.0+, FS depends on the USB sysmodule having been launched in order to mount the SD card. */
if (IsUsbRequiredToMountSdCard()) {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Usb, ncm::StorageId::BuiltInSystem), 0);
}
}
/* Wait for the SD card required services to be ready. */
cfg::WaitSdCardRequiredServicesReady();
/* Wait for other atmosphere mitm modules to initialize. */
R_ABORT_UNLESS(sm::mitm::WaitMitm(sm::ServiceName::Encode("set:sys")));
if (spl::GetSocType() == spl::SocType_Erista) {
if (hos::GetVersion() >= hos::Version_2_0_0) {
R_ABORT_UNLESS(sm::mitm::WaitMitm(sm::ServiceName::Encode("bpc")));
} else {
R_ABORT_UNLESS(sm::mitm::WaitMitm(sm::ServiceName::Encode("bpc:c")));
}
}
/* Launch Atmosphere boot2, using NcmStorageId_None to force SD card boot. */
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Boot2, ncm::StorageId::None), 0);
}
void LaunchPostSdCardBootPrograms() {
/* This code is normally run by boot2. */
/* Launch the usb system module, if we haven't already. */
if (!IsUsbRequiredToMountSdCard()) {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Usb, ncm::StorageId::BuiltInSystem), 0);
}
/* Activate the system fs content storage. */
R_ABORT_UNLESS(ncm::ActivateFsContentStorage(fs::ContentStorageId::System));
/* Find out whether we are maintenance mode. */
const bool maintenance = IsMaintenanceMode();
if (maintenance) {
pm::bm::SetMaintenanceBoot();
}
/* Check for and forward declare non-atmosphere mitm modules. */
DetectAndDeclareFutureMitms();
/* Decide whether to launch tma or htc. */
if (IsHtcEnabled()) {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Htc, ncm::StorageId::None), 0);
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Cs, ncm::StorageId::None), 0);
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::DmntGen2, ncm::StorageId::None), 0);
} else if (IsStandaloneGdbstubEnabled()) {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::DmntGen2, ncm::StorageId::None), 0);
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::BuiltInSystem), 0);
} else {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Dmnt, ncm::StorageId::None), 0);
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::BuiltInSystem), 0);
}
/* Decide whether to launch atmosphere or nintendo's log manager. */
if (IsAtmosphereLogManagerEnabled()) {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::AtmosphereProgramId::AtmosphereLogManager, ncm::StorageId::None), 0);
} else {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::LogManager, ncm::StorageId::BuiltInSystem), 0);
}
/* Launch additional programs. */
if (maintenance) {
LaunchList(AdditionalMaintenanceLaunchPrograms, NumAdditionalMaintenanceLaunchPrograms);
/* Starting in 7.0.0, npns is launched during maintenance boot. */
if (hos::GetVersion() >= hos::Version_7_0_0) {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Npns, ncm::StorageId::BuiltInSystem), 0);
}
} else {
LaunchList(AdditionalLaunchPrograms, NumAdditionalLaunchPrograms);
}
/* Prior to 12.0.0, boot2 was responsible for launching grc and migration. */
if (hos::GetVersion() < hos::Version_12_0_0) {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Grc, ncm::StorageId::BuiltInSystem), 0);
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Migration, ncm::StorageId::BuiltInSystem), 0);
}
/* Launch atmosphere's applet memory service program. */
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::AtmosphereProgramId::AtmosphereMemlet, ncm::StorageId::None), 0);
/* Launch user programs off of the SD. */
LaunchFlaggedProgramsOnSdCard();
}
}

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/>.
*/
#include <stratosphere.hpp>
#include "cal_fs_utils.hpp"
namespace ams::cal {
namespace {
constexpr inline s64 BatteryLotOffset = 0x2CE0;
constexpr inline size_t BatteryLotSize = 0x20;
constexpr inline s64 BatteryVersionOffset = 0x4310;
constexpr inline size_t BatteryVersionSize = 0x10;
constexpr inline size_t BatteryVendorSizeMax = 0x18;
}
Result GetBatteryVersion(u8 *out) {
/* Read the battery version. */
u8 battery_version[BatteryVersionSize];
R_TRY(cal::impl::ReadCalibrationBlock(BatteryVersionOffset, battery_version, sizeof(battery_version)));
/* Write the output. */
*out = battery_version[0];
R_SUCCEED();
}
Result GetBatteryVendor(size_t *out_vendor_size, void *dst, size_t dst_size) {
/* Read the battery lot. */
char battery_lot[BatteryLotSize];
R_TRY(cal::impl::ReadCalibrationBlock(BatteryLotOffset, battery_lot, sizeof(battery_lot)));
/* Copy output. */
*out_vendor_size = static_cast<size_t>(util::Strlcpy(static_cast<char *>(dst), battery_lot, std::min(dst_size, BatteryVendorSizeMax)));
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/>.
*/
#include <stratosphere.hpp>
#include "cal_crc_utils.hpp"
namespace ams::cal::impl {
namespace {
constexpr inline const u16 CrcTable[0x10] = {
0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401,
0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
};
}
u16 CalculateCrc16(const void *data, size_t size) {
AMS_ASSERT(data != nullptr);
u16 crc = 0x55AA;
const u8 *data_u8 = static_cast<const u8 *>(data);
for (size_t i = 0; i < size; ++i) {
crc = (crc >> 4) ^ (CrcTable[crc & 0xF]) ^ (CrcTable[(data_u8[i] >> 0) & 0xF]);
crc = (crc >> 4) ^ (CrcTable[crc & 0xF]) ^ (CrcTable[(data_u8[i] >> 4) & 0xF]);
}
return crc;
}
Result ValidateCalibrationCrc(const void *data, size_t size) {
AMS_ASSERT(data != nullptr);
AMS_ASSERT(size >= sizeof(u16));
const u16 crc = *reinterpret_cast<const u16 *>(reinterpret_cast<uintptr_t>(data) + size - sizeof(u16));
R_UNLESS(CalculateCrc16(data, size - sizeof(u16)) == crc, cal::ResultCalibrationDataCrcError());
R_SUCCEED();
}
}

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::cal::impl {
u16 CalculateCrc16(const void *data, size_t size);
Result ValidateCalibrationCrc(const void *data, size_t size);
}

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 <stratosphere.hpp>
#include "cal_crc_utils.hpp"
#include "cal_fs_utils.hpp"
namespace ams::cal::impl {
Result ReadCalibrationBlock(s64 offset, void *dst, size_t block_size) {
/* Open the calibration binary partition. */
std::unique_ptr<fs::IStorage> storage;
R_TRY(fs::OpenBisPartition(std::addressof(storage), fs::BisPartitionId::CalibrationBinary));
/* Read data from the partition. */
R_TRY(storage->Read(offset, dst, block_size));
/* Validate the crc. */
R_TRY(ValidateCalibrationCrc(dst, block_size));
R_SUCCEED();
}
}

View File

@@ -1,23 +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::cal::impl {
Result ReadCalibrationBlock(s64 offset, void *dst, size_t block_size);
}

View File

@@ -1,83 +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::capsrv {
Result InitializeScreenShotControl() {
#if defined(ATMOSPHERE_OS_HORIZON)
R_RETURN(::capsscInitialize());
#else
AMS_ABORT("TODO");
#endif
}
void FinalizeScreenShotControl() {
#if defined(ATMOSPHERE_OS_HORIZON)
return ::capsscExit();
#else
AMS_ABORT("TODO");
#endif
}
Result CaptureJpegScreenshot(u64 *out_size, void *dst, size_t dst_size, vi::LayerStack layer_stack, TimeSpan timeout) {
#if defined(ATMOSPHERE_OS_HORIZON)
R_RETURN(::capsscCaptureJpegScreenShot(out_size, dst, dst_size, static_cast<::ViLayerStack>(layer_stack), timeout.GetNanoSeconds()));
#else
AMS_UNUSED(out_size, dst, dst_size, layer_stack, timeout);
AMS_ABORT("TODO");
#endif
}
Result OpenRawScreenShotReadStreamForDevelop(size_t *out_data_size, s32 *out_width, s32 *out_height, vi::LayerStack layer_stack, TimeSpan timeout) {
#if defined(ATMOSPHERE_OS_HORIZON)
u64 data_size, width, height;
R_TRY(::capsscOpenRawScreenShotReadStream(std::addressof(data_size), std::addressof(width), std::addressof(height), static_cast<::ViLayerStack>(layer_stack), timeout.GetNanoSeconds()));
*out_data_size = static_cast<size_t>(data_size);
*out_width = static_cast<s32>(width);
*out_height = static_cast<s32>(height);
R_SUCCEED();
#else
AMS_UNUSED(out_data_size, out_width, out_height, layer_stack, timeout);
AMS_ABORT("TODO");
#endif
}
Result ReadRawScreenShotReadStreamForDevelop(size_t *out_read_size, void *dst, size_t dst_size, std::ptrdiff_t offset) {
#if defined(ATMOSPHERE_OS_HORIZON)
u64 read_size;
R_TRY(::capsscReadRawScreenShotReadStream(std::addressof(read_size), dst, dst_size, static_cast<u64>(offset)));
*out_read_size = static_cast<size_t>(read_size);
R_SUCCEED();
#else
AMS_UNUSED(out_read_size, dst, dst_size, offset);
AMS_ABORT("TODO");
#endif
}
void CloseRawScreenShotReadStreamForDevelop() {
#if defined(ATMOSPHERE_OS_HORIZON)
::capsscCloseRawScreenShotReadStream();
#else
AMS_UNUSED();
AMS_ABORT("TODO");
#endif
}
}

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 "decodersrv/decodersrv_decoder_server_object.hpp"
namespace ams::capsrv::server {
namespace {
bool g_initialized = false;
}
Result InitializeForDecoderServer() {
/* Ensure we initialize only once. */
AMS_ABORT_UNLESS(!g_initialized);
/* Clear work memory. */
std::memset(std::addressof(g_work_memory), 0, sizeof(g_work_memory));
/* Initialize the decoder server. */
R_ABORT_UNLESS(g_decoder_control_server_manager.Initialize());
/* Start the server. */
g_decoder_control_server_manager.StartServer();
/* We're initialized. */
g_initialized = true;
R_SUCCEED();
}
void FinalizeForDecoderServer() {
/* Ensure we don't finalize when uninitialized. */
AMS_ABORT_UNLESS(g_initialized);
/* Finalize the server. */
g_decoder_control_server_manager.Finalize();
/* Mark uninitialized. */
g_initialized = false;
}
void DecoderControlServerThreadFunction(void *) {
/* We need to be initialized. */
AMS_ABORT_UNLESS(g_initialized);
/* Run the server. */
g_decoder_control_server_manager.RunServer();
}
}

View File

@@ -1,69 +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 "decodersrv_decoder_server_object.hpp"
namespace ams::capsrv::server {
Result DecoderControlServerManager::Initialize() {
/* Create the objects. */
m_service_holder.emplace();
m_server_manager_holder.emplace();
/* Register the service. */
R_ABORT_UNLESS((m_server_manager_holder->RegisterObjectForServer(m_service_holder->GetShared(), ServiceName, MaxSessions)));
/* Initialize the idle event, we're idle initially. */
os::InitializeEvent(std::addressof(m_idle_event), true, os::EventClearMode_ManualClear);
R_SUCCEED();
}
void DecoderControlServerManager::Finalize() {
/* Check that the server is idle. */
AMS_ASSERT(os::TryWaitEvent(std::addressof(m_idle_event)));
/* Destroy the server. */
os::FinalizeEvent(std::addressof(m_idle_event));
m_server_manager_holder = util::nullopt;
m_service_holder = util::nullopt;
}
void DecoderControlServerManager::StartServer() {
m_server_manager_holder->ResumeProcessing();
}
void DecoderControlServerManager::StopServer() {
/* Request the server stop, and wait until it does. */
m_server_manager_holder->RequestStopProcessing();
os::WaitEvent(std::addressof(m_idle_event));
}
void DecoderControlServerManager::RunServer() {
/* Ensure that we are allowed to run. */
AMS_ABORT_UNLESS(os::TryWaitEvent(std::addressof(m_idle_event)));
/* Clear the event. */
os::ClearEvent(std::addressof(m_idle_event));
/* Process forever. */
m_server_manager_holder->LoopProcess();
/* Signal that we're idle again. */
os::SignalEvent(std::addressof(m_idle_event));
}
}

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>
#include "decodersrv_decoder_control_service.hpp"
namespace ams::capsrv::server {
class DecoderControlServerManager {
public:
/* NOTE: Nintendo only allows one session. */
static constexpr inline size_t NumServers = 1;
static constexpr inline size_t MaxSessions = 2;
static constexpr inline sm::ServiceName ServiceName = sm::ServiceName::Encode("caps:dc");
using ServiceHolderType = ams::sf::UnmanagedServiceObject<capsrv::sf::IDecoderControlService, DecoderControlService>;
using ServerOptions = ams::sf::hipc::DefaultServerManagerOptions;
using ServerManager = ams::sf::hipc::ServerManager<NumServers, ServerOptions, MaxSessions>;
private:
util::optional<ServiceHolderType> m_service_holder;
util::optional<ServerManager> m_server_manager_holder;
os::EventType m_idle_event;
public:
constexpr DecoderControlServerManager() : m_service_holder(), m_server_manager_holder(), m_idle_event{} { /* ... */ }
Result Initialize();
void Finalize();
void StartServer();
void StopServer();
void RunServer();
};
}

View File

@@ -1,150 +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 "decodersrv_decoder_server_object.hpp"
#include "../jpeg/decodersrv_software_jpeg_decoder.hpp"
#include "../jpeg/decodersrv_software_jpeg_shrinker.hpp"
namespace ams::capsrv::server {
namespace {
constexpr const int JpegShrinkQualities[] = {
98, 95, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0
};
Result DecodeJpegImpl(void *dst, size_t dst_size, const void *src_jpeg, size_t src_jpeg_size, u32 width, u32 height, const ScreenShotDecodeOption &option, void *work, size_t work_size) {
/* Clear the work memory. */
std::memset(work, 0, work_size);
ON_SCOPE_EXIT { std::memset(work, 0, work_size); };
/* Clear the output memory. */
std::memset(dst, 0, dst_size);
auto clear_guard = SCOPE_GUARD { std::memset(dst, 0, dst_size); };
/* Validate parameters. */
R_UNLESS(util::IsAligned(width, 0x10), capsrv::ResultAlbumOutOfRange());
R_UNLESS(util::IsAligned(height, 0x4), capsrv::ResultAlbumOutOfRange());
R_UNLESS(dst != nullptr, capsrv::ResultAlbumReadBufferShortage());
R_UNLESS(dst_size >= 4 * width * height, capsrv::ResultAlbumReadBufferShortage());
R_UNLESS(src_jpeg != nullptr, capsrv::ResultAlbumInvalidFileData());
R_UNLESS(src_jpeg_size != 0, capsrv::ResultAlbumInvalidFileData());
/* Create the input. */
const jpeg::SoftwareJpegDecoderInput decode_input = {
.jpeg = src_jpeg,
.jpeg_size = src_jpeg_size,
.width = width,
.height = height,
.fancy_upsampling = option.HasJpegDecoderFlag(ScreenShotDecoderFlag_EnableFancyUpsampling),
.block_smoothing = option.HasJpegDecoderFlag(ScreenShotDecoderFlag_EnableBlockSmoothing),
};
/* Create the output. */
s32 out_width = 0, out_height = 0;
jpeg::SoftwareJpegDecoderOutput decode_output = {
.out_width = std::addressof(out_width),
.out_height = std::addressof(out_height),
.dst = dst,
.dst_size = dst_size,
};
/* Decode the jpeg. */
R_TRY(jpeg::SoftwareJpegDecoder::DecodeRgba8(decode_output, decode_input, work, work_size));
/* We succeeded, so we shouldn't clear the output memory. */
clear_guard.Cancel();
R_SUCCEED();
}
Result ShrinkJpegImpl(u64 *out_size, void *dst, size_t dst_size, const void *src_jpeg, size_t src_jpeg_size, u32 width, u32 height, const ScreenShotDecodeOption &option, void *work, size_t work_size) {
/* Validate parameters. */
R_UNLESS(util::IsAligned(width, 0x10), capsrv::ResultAlbumOutOfRange());
R_UNLESS(util::IsAligned(height, 0x4), capsrv::ResultAlbumOutOfRange());
R_UNLESS(dst != nullptr, capsrv::ResultInternalJpegOutBufferShortage());
R_UNLESS(dst_size != 0, capsrv::ResultAlbumReadBufferShortage());
R_UNLESS(src_jpeg != nullptr, capsrv::ResultAlbumInvalidFileData());
R_UNLESS(src_jpeg_size != 0, capsrv::ResultAlbumInvalidFileData());
/* Create the input. */
const jpeg::SoftwareJpegShrinkerInput shrink_input = {
.jpeg = src_jpeg,
.jpeg_size = src_jpeg_size,
.width = width,
.height = height,
.fancy_upsampling = option.HasJpegDecoderFlag(ScreenShotDecoderFlag_EnableFancyUpsampling),
.block_smoothing = option.HasJpegDecoderFlag(ScreenShotDecoderFlag_EnableBlockSmoothing),
};
/* Create the output. */
u64 shrunk_size = 0;
s32 shrunk_width = 0, shrunk_height = 0;
jpeg::SoftwareJpegShrinkerOutput shrink_output = {
.out_size = std::addressof(shrunk_size),
.out_width = std::addressof(shrunk_width),
.out_height = std::addressof(shrunk_height),
.dst = dst,
.dst_size = dst_size,
};
/* Try to shrink the jpeg at various quality levels. */
for (auto quality : JpegShrinkQualities) {
/* Shrink at the current quality. */
R_TRY_CATCH(jpeg::SoftwareJpegShrinker::ShrinkRgba8(shrink_output, shrink_input, quality, work, work_size)) {
/* If the output buffer isn't large enough to fit the output, we should try at a lower quality. */
R_CATCH(capsrv::ResultInternalJpegOutBufferShortage) {
continue;
}
/* Nintendo doesn't catch this result, but our lack of work buffer use makes me think this may be necessary. */
R_CATCH(capsrv::ResultInternalJpegWorkMemoryShortage) {
continue;
}
} R_END_TRY_CATCH;
/* Write the output size. */
*out_size = shrunk_size;
R_SUCCEED();
}
/* Nintendo aborts if no quality succeeds. */
AMS_ABORT("ShrinkJpeg should succeed before this point\n");
}
}
Result DecoderControlService::DecodeJpeg(const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const ScreenShotDecodeOption &option) {
/* Get the work buffer. */
void *work = g_work_memory.jpeg_decoder_memory;
size_t work_size = sizeof(g_work_memory.jpeg_decoder_memory);
/* Call the decoder implementation. */
R_RETURN(DecodeJpegImpl(out.GetPointer(), out.GetSize(), in.GetPointer(), in.GetSize(), width, height, option, work, work_size));
}
Result DecoderControlService::ShrinkJpeg(ams::sf::Out<u64> out_size, const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const capsrv::ScreenShotDecodeOption &option) {
/* Get the work buffer. */
void *work = g_work_memory.jpeg_decoder_memory;
size_t work_size = sizeof(g_work_memory.jpeg_decoder_memory);
/* Call the shrink implementation. */
R_RETURN(ShrinkJpegImpl(out_size.GetPointer(), out.GetPointer(), out.GetSize(), in.GetPointer(), in.GetSize(), width, height, option, work, work_size));
}
}

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_CAPSRV_DECODER_CONTROL_SERVICE_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 3001, Result, DecodeJpeg, (const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const capsrv::ScreenShotDecodeOption &option), (out, in, width, height, option)) \
AMS_SF_METHOD_INFO(C, H, 4001, Result, ShrinkJpeg, (ams::sf::Out<u64> out_size, const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const capsrv::ScreenShotDecodeOption &option), (out_size, out, in, width, height, option))
AMS_SF_DEFINE_INTERFACE(ams::capsrv::sf, IDecoderControlService, AMS_CAPSRV_DECODER_CONTROL_SERVICE_INTERFACE_INFO, 0xD168E90B)
namespace ams::capsrv::server {
class DecoderControlService final {
public:
Result DecodeJpeg(const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const ScreenShotDecodeOption &option);
Result ShrinkJpeg(ams::sf::Out<u64> out_size, const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const capsrv::ScreenShotDecodeOption &option);
};
static_assert(capsrv::sf::IsIDecoderControlService<DecoderControlService>);
}

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/>.
*/
#include <stratosphere.hpp>
#include "decodersrv_decoder_server_object.hpp"
namespace ams::capsrv::server {
/* Instantiate the decoder server globals in a single TU. */
DecoderWorkMemory g_work_memory;
DecoderControlServerManager g_decoder_control_server_manager;
}

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>
#include "decodersrv_decoder_work_memory.hpp"
#include "decodersrv_decoder_control_server_manager.hpp"
namespace ams::capsrv::server {
extern DecoderWorkMemory g_work_memory;
extern DecoderControlServerManager g_decoder_control_server_manager;
}

View File

@@ -1,28 +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::capsrv::server {
struct DecoderWorkMemory {
alignas(os::MemoryPageSize) u8 jpeg_decoder_memory[SoftwareJpegDecoderWorkMemorySize];
};
static_assert(sizeof(DecoderWorkMemory) == SoftwareJpegDecoderWorkMemorySize);
static_assert(alignof(DecoderWorkMemory) == os::MemoryPageSize);
static_assert(util::is_pod<DecoderWorkMemory>::value);
}

View File

@@ -1,59 +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 <csetjmp>
#include "capsrv_server_jpeg_library_types.hpp"
namespace ams::capsrv::server::jpeg {
struct JpegErrorHandler : public JpegLibraryType::jpeg_error_mgr {
public:
std::jmp_buf jmp_buf;
Result result;
public:
static void HandleError(JpegLibraryType::jpeg_common_struct *common) {
/* Retrieve the handler. */
JpegErrorHandler *handler = reinterpret_cast<JpegErrorHandler *>(common->err);
/* Set the result. */
handler->result = GetResult(handler->msg_code, handler->msg_parm.i[0]);
/* Return to the caller. */
longjmp(handler->jmp_buf, -1);
}
static Result GetResult(int msg_code, int msg_param) {
/* NOTE: Nintendo uses msg_param for error codes that we never trigger. */
/* TODO: Fully support all J_MESSAGE_CODEs that Nintendo handles? */
AMS_UNUSED(msg_param);
switch (msg_code) {
case JpegLibraryType::J_MESSAGE_CODE::JERR_BUFFER_SIZE:
case JpegLibraryType::J_MESSAGE_CODE::JERR_NO_BACKING_STORE:
case JpegLibraryType::J_MESSAGE_CODE::JERR_OUT_OF_MEMORY:
case JpegLibraryType::J_MESSAGE_CODE::JERR_TFILE_CREATE:
case JpegLibraryType::J_MESSAGE_CODE::JERR_TFILE_READ:
case JpegLibraryType::J_MESSAGE_CODE::JERR_TFILE_SEEK:
case JpegLibraryType::J_MESSAGE_CODE::JERR_TFILE_WRITE:
R_THROW(capsrv::ResultInternalJpegWorkMemoryShortage());
default:
R_THROW(capsrv::ResultInternalJpegEncoderError());
}
}
};
}

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>
#include <jpeglib.h>
#include <jerror.h>
namespace ams::capsrv::server::jpeg {
class JpegLibraryType {
public:
using jpeg_common_struct = ::jpeg_common_struct;
using jpeg_compress_struct = ::jpeg_compress_struct;
using jpeg_decompress_struct = ::jpeg_decompress_struct;
using jpeg_error_mgr = ::jpeg_error_mgr;
using jpeg_destination_mgr = ::jpeg_destination_mgr;
using j_common_ptr = ::j_common_ptr;
using j_compress_ptr = ::j_compress_ptr;
using boolean = ::boolean;
using JOCTET = ::JOCTET;
using JDIMENSION = ::JDIMENSION;
using JSAMPARRAY = ::JSAMPARRAY;
using JSAMPROW = ::JSAMPROW;
using J_COLOR_SPACE = ::J_COLOR_SPACE;
using J_DCT_METHOD = ::J_DCT_METHOD;
using J_MESSAGE_CODE = ::J_MESSAGE_CODE;
};
}

View File

@@ -1,185 +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 "decodersrv_software_jpeg_decoder.hpp"
#include "capsrv_server_jpeg_library_types.hpp"
#include "capsrv_server_jpeg_error_handler.hpp"
namespace ams::capsrv::server::jpeg {
#define CAPSRV_ABORT_UNLESS(expr) do { \
const bool __capsrv_assert_res = (expr); \
AMS_ASSERT(__capsrv_assert_res); \
AMS_ABORT_UNLESS(__capsrv_assert_res); \
} while (0)
#define CAPSRV_ASSERT(expr) do { \
const bool __capsrv_assert_res = (expr); \
AMS_ASSERT(__capsrv_assert_res); \
R_UNLESS(__capsrv_assert_res, capsrv::ResultAlbumError()); \
} while (0)
namespace {
constexpr s32 ImageSizeHorizonalUnit = 0x10;
constexpr s32 ImageSizeVerticalUnit = 0x4;
constexpr s32 RgbColorComponentCount = 3;
constexpr s32 RgbaColorComponentCount = 4;
Result GetRgbBufferSize(size_t *out_size, size_t *out_stride, s32 width, size_t work_size) {
/* Calculate the space we need and verify we have enough. */
const size_t rgb_width = util::AlignUp(static_cast<size_t>(width), ImageSizeHorizonalUnit);
const size_t rgb_stride = rgb_width * RgbColorComponentCount;
const size_t rgb_size = rgb_stride * ImageSizeVerticalUnit;
R_UNLESS(work_size >= rgb_size, capsrv::ResultInternalJpegWorkMemoryShortage());
/* Return the output to the caller. */
*out_size = rgb_size;
*out_stride = rgb_stride;
R_SUCCEED();
}
}
Result SoftwareJpegDecoder::DecodeRgba8(SoftwareJpegDecoderOutput &output, const SoftwareJpegDecoderInput &input, void *work, size_t work_size) {
CAPSRV_ABORT_UNLESS(util::IsAligned(input.width, ImageSizeHorizonalUnit));
CAPSRV_ABORT_UNLESS(util::IsAligned(input.height, ImageSizeVerticalUnit));
CAPSRV_ABORT_UNLESS(output.dst != nullptr);
CAPSRV_ABORT_UNLESS(output.dst_size >= static_cast<size_t>(RgbaColorComponentCount * input.width * input.height));
CAPSRV_ABORT_UNLESS(output.out_width != nullptr);
CAPSRV_ABORT_UNLESS(output.out_height != nullptr);
/* Determine work buffer extents. */
char *work_start = static_cast<char *>(work);
char *work_end = work_start + work_size;
/* Determine the buffer extents for our linebuffers. */
u8 *rgb_buffer = static_cast<u8 *>(static_cast<void *>(work_start));
size_t rgb_buffer_size;
size_t rgb_buffer_stride;
R_TRY(GetRgbBufferSize(std::addressof(rgb_buffer_size), std::addressof(rgb_buffer_stride), input.width, work_size));
/* The start of the workbuffer is reserved for linebuffer space. */
work_start += rgb_buffer_size;
/* Create our decompression structure. */
JpegLibraryType::jpeg_decompress_struct cinfo = {};
/* Here nintendo creates a work buffer structure containing work_start + work_size. */
/* This seems to be a custom patch for/to libjpeg-turbo. */
/* It would be desirable for us to mimic this, because it gives Nintendo strong */
/* fixed memory usage guarantees. */
/* TODO: Determine if it is feasible for us to recreate this ourselves, */
/* Either by adding support to the devkitPro libjpeg-turbo portlib or otherwise. */
AMS_UNUSED(work_end);
/* Create our error manager. */
JpegErrorHandler jerr = {
.result = ResultSuccess(),
};
jerr.error_exit = JpegErrorHandler::HandleError,
/* Link our error manager to our decompression structure. */
cinfo.err = jpeg_std_error(std::addressof(jerr));
/* Use setjmp, so that on error our handler will longjmp to return an error result. */
if (setjmp(jerr.jmp_buf) == 0) {
/* Create our decompressor. */
jpeg_create_decompress(std::addressof(cinfo));
ON_SCOPE_EXIT { jpeg_destroy_decompress(std::addressof(cinfo)); };
/* Setup our memory reader, ensure the header is correct. */
jpeg_mem_src(std::addressof(cinfo), const_cast<unsigned char *>(static_cast<const unsigned char *>(input.jpeg)), input.jpeg_size);
R_UNLESS(jpeg_read_header(std::addressof(cinfo), true) == JPEG_HEADER_OK, capsrv::ResultAlbumInvalidFileData());
/* Ensure width and height are correct. */
R_UNLESS(cinfo.image_width == input.width, capsrv::ResultAlbumInvalidFileData());
R_UNLESS(cinfo.image_height == input.height, capsrv::ResultAlbumInvalidFileData());
/* Set output parameters. */
cinfo.out_color_space = JpegLibraryType::J_COLOR_SPACE::JCS_RGB;
cinfo.dct_method = JpegLibraryType::J_DCT_METHOD::JDCT_ISLOW;
cinfo.do_fancy_upsampling = input.fancy_upsampling;
cinfo.do_block_smoothing = input.block_smoothing;
/* Start decompression. */
R_UNLESS(jpeg_start_decompress(std::addressof(cinfo)) == TRUE, capsrv::ResultAlbumInvalidFileData());
/* Check the parameters. */
CAPSRV_ASSERT(cinfo.output_width == input.width);
CAPSRV_ASSERT(cinfo.output_height == input.height);
CAPSRV_ASSERT(cinfo.out_color_components == RgbColorComponentCount);
CAPSRV_ASSERT(cinfo.output_components == RgbColorComponentCount);
/* Parse the scanlines. */
{
/* Convert our destination to a writable u8 buffer. */
u8 *dst = static_cast<u8 *>(output.dst);
/* Create our linebuffer structure. */
JpegLibraryType::JSAMPROW linebuffers[ImageSizeVerticalUnit] = {};
for (int i = 0; i < ImageSizeVerticalUnit; i++) {
linebuffers[i] = rgb_buffer + rgb_buffer_stride * i;
}
/* While we still have scanlines, parse! */
while (cinfo.output_scanline < input.height) {
/* Decode scanlines. */
int num_scanlines = jpeg_read_scanlines(std::addressof(cinfo), linebuffers, ImageSizeVerticalUnit);
CAPSRV_ASSERT(num_scanlines <= ImageSizeVerticalUnit);
/* Write out line by line. */
for (s32 i = 0; i < num_scanlines; i++) {
const u8 *src = linebuffers[i];
for (s32 j = 0; j < static_cast<s32>(input.width); j++) {
/* Write the output. */
/* First R, */
*(dst++) = *(src++);
/* Then G, */
*(dst++) = *(src++);
/* Then B, */
*(dst++) = *(src++);
/* Then A. */
*(dst++) = 0xFF;
}
}
}
}
/* Finish the decompression. */
R_UNLESS(jpeg_finish_decompress(std::addressof(cinfo)) == TRUE, capsrv::ResultAlbumInvalidFileData());
} else {
/* Some unknown error was caught by our handler. */
R_THROW(capsrv::ResultAlbumInvalidFileData());
}
/* Write the size we decoded to output. */
*output.out_width = static_cast<s32>(cinfo.output_width);
*output.out_width = static_cast<s32>(cinfo.output_height);
R_SUCCEED();
}
}

View File

@@ -1,42 +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::capsrv::server::jpeg {
struct SoftwareJpegDecoderInput {
const void *jpeg;
size_t jpeg_size;
u32 width;
u32 height;
bool fancy_upsampling;
bool block_smoothing;
};
struct SoftwareJpegDecoderOutput {
s32 *out_width;
s32 *out_height;
void *dst;
size_t dst_size;
};
class SoftwareJpegDecoder {
public:
static Result DecodeRgba8(SoftwareJpegDecoderOutput &output, const SoftwareJpegDecoderInput &input, void *work, size_t work_size);
};
}

View File

@@ -1,236 +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 "decodersrv_software_jpeg_shrinker.hpp"
#include "capsrv_server_jpeg_library_types.hpp"
#include "capsrv_server_jpeg_error_handler.hpp"
namespace ams::capsrv::server::jpeg {
#define CAPSRV_ABORT_UNLESS(expr) do { \
const bool __capsrv_assert_res = (expr); \
AMS_ASSERT(__capsrv_assert_res); \
AMS_ABORT_UNLESS(__capsrv_assert_res); \
} while (0)
#define CAPSRV_ASSERT(expr) do { \
const bool __capsrv_assert_res = (expr); \
AMS_ASSERT(__capsrv_assert_res); \
R_UNLESS(__capsrv_assert_res, capsrv::ResultAlbumError()); \
} while (0)
namespace {
constexpr s32 ImageSizeHorizonalUnit = 0x10;
constexpr s32 ImageSizeVerticalUnitDecode = 0x4;
constexpr s32 ImageSizeVerticalUnitEncode = 0x8;
constexpr s32 RgbColorComponentCount = 3;
constexpr s32 RgbaColorComponentCount = 4;
Result GetRgbBufferSize(size_t *out_size, size_t *out_stride, s32 width, size_t work_size) {
/* Calculate the space we need and verify we have enough. */
const size_t rgb_width = util::AlignUp(static_cast<size_t>(width), ImageSizeHorizonalUnit);
const size_t rgb_stride = rgb_width * RgbColorComponentCount;
const size_t rgb_size = rgb_stride * ImageSizeVerticalUnitEncode;
R_UNLESS(work_size >= rgb_size, capsrv::ResultInternalJpegWorkMemoryShortage());
/* Return the output to the caller. */
*out_size = rgb_size;
*out_stride = rgb_stride;
R_SUCCEED();
}
void SetupEncodingParameter(JpegLibraryType::jpeg_compress_struct *cinfo, const SoftwareJpegShrinkerInput &input, int quality) {
/* Set parameters. */
cinfo->image_width = static_cast<JpegLibraryType::JDIMENSION>(input.width / 2);
cinfo->image_height = static_cast<JpegLibraryType::JDIMENSION>(input.height / 2);
cinfo->input_components = RgbColorComponentCount;
cinfo->in_color_space = JpegLibraryType::J_COLOR_SPACE::JCS_RGB;
/* Set defaults/color space. */
jpeg_set_defaults(cinfo);
jpeg_set_colorspace(cinfo, JpegLibraryType::J_COLOR_SPACE::JCS_YCbCr);
/* Configure sampling. */
/* libjpeg-turbo doesn't actually have this field, as of now. */
/* cinfo->do_fancy_downsampling = false; */
cinfo->comp_info[0].h_samp_factor = 2;
cinfo->comp_info[0].v_samp_factor = 1;
cinfo->comp_info[1].h_samp_factor = 1;
cinfo->comp_info[1].v_samp_factor = 1;
cinfo->comp_info[2].h_samp_factor = 1;
cinfo->comp_info[2].v_samp_factor = 1;
/* Set the quality. */
jpeg_set_quality(cinfo, quality, true);
/* Configure remaining parameters. */
cinfo->dct_method = JpegLibraryType::J_DCT_METHOD::JDCT_ISLOW;
cinfo->optimize_coding = false;
cinfo->write_JFIF_header = true;
}
}
Result SoftwareJpegShrinker::ShrinkRgba8(SoftwareJpegShrinkerOutput &output, const SoftwareJpegShrinkerInput &input, int quality, void *work, size_t work_size) {
CAPSRV_ABORT_UNLESS(util::IsAligned(input.width, ImageSizeHorizonalUnit));
CAPSRV_ABORT_UNLESS(util::IsAligned(input.height, ImageSizeVerticalUnitDecode));
const u32 shrunk_width = input.width / 2;
const u32 shrunk_height = input.height / 2;
CAPSRV_ABORT_UNLESS(util::IsAligned(shrunk_width, ImageSizeHorizonalUnit));
CAPSRV_ABORT_UNLESS(output.dst != nullptr);
CAPSRV_ABORT_UNLESS(output.out_width != nullptr);
CAPSRV_ABORT_UNLESS(output.out_height != nullptr);
/* Determine work buffer extents. */
char *work_start = static_cast<char *>(work);
char *work_end = work_start + work_size;
/* Determine the buffer extents for our linebuffers. */
u8 *rgb_buffer = static_cast<u8 *>(static_cast<void *>(work_start));
size_t rgb_buffer_size;
size_t rgb_buffer_stride;
R_TRY(GetRgbBufferSize(std::addressof(rgb_buffer_size), std::addressof(rgb_buffer_stride), input.width, work_size));
/* The start of the workbuffer is reserved for linebuffer space. */
work_start += rgb_buffer_size;
/* Create our compression structures. */
JpegLibraryType::jpeg_decompress_struct dcinfo = {};
JpegLibraryType::jpeg_compress_struct ecinfo = {};
/* Here nintendo creates a work buffer structure containing work_start + work_size. */
/* This seems to be a custom patch for/to libjpeg-turbo. */
/* It would be desirable for us to mimic this, because it gives Nintendo strong */
/* fixed memory usage guarantees. */
/* TODO: Determine if it is feasible for us to recreate this ourselves, */
/* Either by adding support to the devkitPro libjpeg-turbo portlib or otherwise. */
AMS_UNUSED(work_end);
/* Create our error managers. */
JpegErrorHandler jerr_dc = { .result = ResultSuccess(), };
JpegErrorHandler jerr_ec = { .result = ResultSuccess(), };
jerr_dc.error_exit = JpegErrorHandler::HandleError,
jerr_ec.error_exit = JpegErrorHandler::HandleError,
/* Link our error managers to our compression structures. */
dcinfo.err = jpeg_std_error(std::addressof(jerr_dc));
ecinfo.err = jpeg_std_error(std::addressof(jerr_ec));
/* Use setjmp, so that on error our handler will longjmp to return an error result. */
if (setjmp(jerr_ec.jmp_buf) == 0) {
if (setjmp(jerr_dc.jmp_buf) == 0) {
/* Create our decompressor. */
jpeg_create_decompress(std::addressof(dcinfo));
ON_SCOPE_EXIT { jpeg_destroy_decompress(std::addressof(dcinfo)); };
/* Setup our memory reader, ensure the header is correct. */
jpeg_mem_src(std::addressof(dcinfo), const_cast<unsigned char *>(static_cast<const unsigned char *>(input.jpeg)), input.jpeg_size);
R_UNLESS(jpeg_read_header(std::addressof(dcinfo), true) == JPEG_HEADER_OK, capsrv::ResultAlbumInvalidFileData());
/* Ensure width and height are correct. */
R_UNLESS(dcinfo.image_width == input.width, capsrv::ResultAlbumInvalidFileData());
R_UNLESS(dcinfo.image_height == input.height, capsrv::ResultAlbumInvalidFileData());
/* Set output parameters. */
dcinfo.out_color_space = JpegLibraryType::J_COLOR_SPACE::JCS_RGB;
dcinfo.dct_method = JpegLibraryType::J_DCT_METHOD::JDCT_ISLOW;
dcinfo.do_fancy_upsampling = input.fancy_upsampling;
dcinfo.do_block_smoothing = input.block_smoothing;
dcinfo.scale_num = 1;
dcinfo.scale_denom = 2;
/* Start decompression. */
R_UNLESS(jpeg_start_decompress(std::addressof(dcinfo)) == TRUE, capsrv::ResultAlbumInvalidFileData());
/* Check the parameters. */
CAPSRV_ASSERT(dcinfo.output_width == shrunk_width);
CAPSRV_ASSERT(dcinfo.output_height == shrunk_height);
CAPSRV_ASSERT(dcinfo.out_color_components == RgbColorComponentCount);
CAPSRV_ASSERT(dcinfo.output_components == RgbColorComponentCount);
/* Create our compressor. */
jpeg_create_compress(std::addressof(ecinfo));
ON_SCOPE_EXIT { jpeg_destroy_compress(std::addressof(ecinfo)); };
/* Setup our memory writer. */
unsigned long out_size = static_cast<unsigned long>(output.dst_size);
jpeg_mem_dest(std::addressof(ecinfo), reinterpret_cast<unsigned char **>(std::addressof(output.dst)), std::addressof(out_size));
/* Setup the encoding parameters. */
SetupEncodingParameter(std::addressof(ecinfo), input, quality);
/* Start compression. */
jpeg_start_compress(std::addressof(ecinfo), true);
/* Parse the scanlines. */
{
/* Create our linebuffer structure. */
JpegLibraryType::JSAMPROW linebuffers[ImageSizeVerticalUnitEncode] = {};
for (int i = 0; i < ImageSizeVerticalUnitEncode; i++) {
linebuffers[i] = rgb_buffer + rgb_buffer_stride * i;
}
/* While we still have scanlines, parse! */
while (dcinfo.output_scanline < shrunk_height) {
/* Determine remaining scanlines. */
const auto remaining_scanlines = shrunk_height - dcinfo.output_scanline;
const auto cur_max_scanlines = std::min<s32>(remaining_scanlines, ImageSizeVerticalUnitEncode);
/* If we have scanlines to decode, try to do so. */
auto writable_scanlines = 0;
while (writable_scanlines < cur_max_scanlines) {
const auto decoded = jpeg_read_scanlines(std::addressof(dcinfo), linebuffers + writable_scanlines, ImageSizeVerticalUnitDecode);
CAPSRV_ASSERT(decoded <= ImageSizeVerticalUnitDecode / 2);
writable_scanlines += decoded;
}
/* If we have scanlines to write, try to do so. */
jpeg_write_scanlines(std::addressof(ecinfo), linebuffers, writable_scanlines);
}
}
/* Finish the decompression. */
R_UNLESS(jpeg_finish_decompress(std::addressof(dcinfo)) == TRUE, capsrv::ResultAlbumInvalidFileData());
/* Finish the compression. */
jpeg_finish_compress(std::addressof(ecinfo));
/* Set the output size. */
*output.out_size = out_size;
} else {
/* Some unknown error was caught by our handler. */
R_THROW(capsrv::ResultAlbumInvalidFileData());
}
} else {
/* Return the encoding result. */
R_THROW(jerr_ec.result);
}
/* Write the size we decoded to output. */
*output.out_width = static_cast<s32>(dcinfo.output_width);
*output.out_width = static_cast<s32>(dcinfo.output_height);
R_SUCCEED();
}
}

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::capsrv::server::jpeg {
struct SoftwareJpegShrinkerInput {
const void *jpeg;
size_t jpeg_size;
u32 width;
u32 height;
bool fancy_upsampling;
bool block_smoothing;
};
struct SoftwareJpegShrinkerOutput {
u64 *out_size;
s32 *out_width;
s32 *out_height;
void *dst;
size_t dst_size;
};
class SoftwareJpegShrinker {
public:
static Result ShrinkRgba8(SoftwareJpegShrinkerOutput &output, const SoftwareJpegShrinkerInput &input, int quality, void *work, size_t work_size);
};
}

View File

@@ -1,106 +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::cfg {
namespace {
std::atomic<u32> g_flag_mount_count;
/* Helper. */
void GetFlagMountName(char *dst) {
util::SNPrintf(dst, fs::MountNameLengthMax + 1, "#flag%08x", g_flag_mount_count.fetch_add(1));
}
bool HasFlagFile(const char *flag_path) {
/* All flags are not present until the SD card is. */
if (!IsSdCardInitialized()) {
return false;
}
/* Mount the SD card. */
char mount_name[fs::MountNameLengthMax + 1];
GetFlagMountName(mount_name);
if (R_FAILED(fs::MountSdCard(mount_name))) {
return false;
}
ON_SCOPE_EXIT { fs::Unmount(mount_name); };
/* Check if the entry exists. */
char full_path[fs::EntryNameLengthMax + 1];
util::SNPrintf(full_path, sizeof(full_path), "%s:/%s", mount_name, flag_path[0] == '/' ? flag_path + 1 : flag_path);
bool has_file;
if (R_FAILED(fs::HasFile(std::addressof(has_file), full_path))) {
return false;
}
return has_file;
}
Result DeleteFlagFile(const char *flag_path) {
/* We need the SD card to be available to delete anything. */
AMS_ABORT_UNLESS(IsSdCardInitialized());
/* Mount the sd card. */
char mount_name[fs::MountNameLengthMax + 1];
GetFlagMountName(mount_name);
R_TRY(fs::MountSdCard(mount_name));
ON_SCOPE_EXIT { fs::Unmount(mount_name); };
/* Get the flag path. */
char full_path[fs::EntryNameLengthMax + 1];
util::SNPrintf(full_path, sizeof(full_path), "%s:/%s", mount_name, flag_path[0] == '/' ? flag_path + 1 : flag_path);
/* Delete the file. */
R_TRY(fs::DeleteFile(full_path));
return ResultSuccess();
}
}
/* Flag utilities. */
bool HasFlag(const sm::MitmProcessInfo &process_info, const char *flag) {
return HasContentSpecificFlag(process_info.program_id, flag) || (process_info.override_status.IsHbl() && HasHblFlag(flag));
}
bool HasContentSpecificFlag(ncm::ProgramId program_id, const char *flag) {
char content_flag[fs::EntryNameLengthMax + 1];
util::SNPrintf(content_flag, sizeof(content_flag) - 1, "/atmosphere/contents/%016" PRIx64 "/flags/%s.flag", program_id.value, flag);
return HasFlagFile(content_flag);
}
bool HasGlobalFlag(const char *flag) {
char global_flag[fs::EntryNameLengthMax + 1];
util::SNPrintf(global_flag, sizeof(global_flag) - 1, "/atmosphere/flags/%s.flag", flag);
return HasFlagFile(global_flag);
}
bool HasHblFlag(const char *flag) {
char hbl_flag[0x100];
util::SNPrintf(hbl_flag, sizeof(hbl_flag) - 1, "hbl_%s", flag);
return HasGlobalFlag(hbl_flag);
}
Result DeleteGlobalFlag(const char *flag) {
char global_flag[fs::EntryNameLengthMax + 1];
util::SNPrintf(global_flag, sizeof(global_flag) - 1, "/atmosphere/flags/%s.flag", flag);
return DeleteFlagFile(global_flag);
}
}

View File

@@ -1,22 +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>
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
#include "cfg_flags.board.nintendo_nx.inc"
#else
#include "cfg_flags.generic.inc"
#endif

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/>.
*/
#include <stratosphere.hpp>
namespace ams::cfg {
/* Flag utilities. */
bool HasFlag(const sm::MitmProcessInfo &process_info, const char *flag) {
AMS_UNUSED(process_info, flag);
return false;
}
bool HasContentSpecificFlag(ncm::ProgramId program_id, const char *flag) {
AMS_UNUSED(program_id, flag);
return false;
}
bool HasGlobalFlag(const char *flag) {
AMS_UNUSED(flag);
return false;
}
bool HasHblFlag(const char *flag) {
AMS_UNUSED(flag);
return false;
}
Result DeleteGlobalFlag(const char *flag) {
AMS_UNUSED(flag);
return false;
}
}

View File

@@ -1,443 +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::cfg {
namespace {
/* Types. */
struct OverrideKey {
u64 key_combination;
bool override_by_default;
};
struct ProgramOverrideKey {
OverrideKey override_key;
ncm::ProgramId program_id;
};
constexpr ProgramOverrideKey InvalidProgramOverrideKey = {};
constexpr ProgramOverrideKey DefaultAppletPhotoViewerOverrideKey = {
.override_key = {
.key_combination = HidNpadButton_R,
.override_by_default = true,
},
.program_id = ncm::SystemAppletId::PhotoViewer,
};
constexpr size_t MaxProgramOverrideKeys = 8;
struct HblOverrideConfig {
ProgramOverrideKey program_configs[MaxProgramOverrideKeys];
impl::OverrideStatusFlag program_as_flags[MaxProgramOverrideKeys];
OverrideKey override_any_app_key;
impl::OverrideStatusFlag override_any_app_as_flag;
bool override_any_app;
};
struct ContentSpecificOverrideConfig {
OverrideKey override_key;
OverrideKey cheat_enable_key;
OverrideLocale locale;
};
/* Override globals. */
OverrideKey g_default_override_key = {
.key_combination = HidNpadButton_L,
.override_by_default = true,
};
OverrideKey g_default_cheat_enable_key = {
.key_combination = HidNpadButton_L,
.override_by_default = true,
};
HblOverrideConfig g_hbl_override_config = {
.program_configs = {
DefaultAppletPhotoViewerOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
},
.program_as_flags = {
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
},
.override_any_app_key = {
.key_combination = HidNpadButton_R,
.override_by_default = false,
},
.override_any_app_as_flag = impl::OverrideStatusFlag_AddressSpace64Bit,
.override_any_app = true,
};
char g_hbl_sd_path[0x100] = "/atmosphere/hbl.nsp";
/* Helpers. */
OverrideKey ParseOverrideKey(const char *value) {
OverrideKey cfg = {};
/* Parse on by default. */
if (value[0] == '!') {
cfg.override_by_default = true;
value++;
}
/* Parse key combination. */
if (strcasecmp(value, "A") == 0) {
cfg.key_combination = HidNpadButton_A;
} else if (strcasecmp(value, "B") == 0) {
cfg.key_combination = HidNpadButton_B;
} else if (strcasecmp(value, "X") == 0) {
cfg.key_combination = HidNpadButton_X;
} else if (strcasecmp(value, "Y") == 0) {
cfg.key_combination = HidNpadButton_Y;
} else if (strcasecmp(value, "LS") == 0) {
cfg.key_combination = HidNpadButton_StickL;
} else if (strcasecmp(value, "RS") == 0) {
cfg.key_combination = HidNpadButton_StickR;
} else if (strcasecmp(value, "L") == 0) {
cfg.key_combination = HidNpadButton_L;
} else if (strcasecmp(value, "R") == 0) {
cfg.key_combination = HidNpadButton_R;
} else if (strcasecmp(value, "ZL") == 0) {
cfg.key_combination = HidNpadButton_ZL;
} else if (strcasecmp(value, "ZR") == 0) {
cfg.key_combination = HidNpadButton_ZR;
} else if (strcasecmp(value, "PLUS") == 0) {
cfg.key_combination = HidNpadButton_Plus;
} else if (strcasecmp(value, "MINUS") == 0) {
cfg.key_combination = HidNpadButton_Minus;
} else if (strcasecmp(value, "DLEFT") == 0) {
cfg.key_combination = HidNpadButton_Left;
} else if (strcasecmp(value, "DUP") == 0) {
cfg.key_combination = HidNpadButton_Up;
} else if (strcasecmp(value, "DRIGHT") == 0) {
cfg.key_combination = HidNpadButton_Right;
} else if (strcasecmp(value, "DDOWN") == 0) {
cfg.key_combination = HidNpadButton_Down;
} else if (strcasecmp(value, "SL") == 0) {
cfg.key_combination = HidNpadButton_AnySL;
} else if (strcasecmp(value, "SR") == 0) {
cfg.key_combination = HidNpadButton_AnySR;
}
return cfg;
}
impl::OverrideStatusFlag ParseOverrideAddressSpace(const char *value) {
if (strcasecmp(value, "39_bit") == 0 || strcasecmp(value, "39") == 0) {
return impl::OverrideStatusFlag_AddressSpace64Bit;
} else if (strcasecmp(value, "36_bit") == 0 || strcasecmp(value, "36") == 0) {
return impl::OverrideStatusFlag_AddressSpace64BitDeprecated;
} else if (strcasecmp(value, "32_bit") == 0 || strcasecmp(value, "32") == 0) {
return impl::OverrideStatusFlag_AddressSpace32Bit;
} else if (strcasecmp(value, "32_bit_without_alias") == 0 ||
strcasecmp(value, "32_bit_no_alias") == 0 ||
strcasecmp(value, "32_without_alias") == 0 ||
strcasecmp(value, "32_no_alias") ||
strcasecmp(value, "32_bit_without_map") == 0 ||
strcasecmp(value, "32_bit_no_map") == 0 ||
strcasecmp(value, "32_without_map") == 0 ||
strcasecmp(value, "32_no_map") == 0)
{
return impl::OverrideStatusFlag_AddressSpace32BitWithoutAlias;
} else {
/* Default to 39-bit. */
return impl::OverrideStatusFlag_AddressSpace64Bit;
}
}
inline void SetHblSpecificProgramId(size_t i, const char *value) {
g_hbl_override_config.program_configs[i].program_id = {strtoul(value, nullptr, 16)};
}
inline void SetHblSpecificOverrideKey(size_t i, const char *value) {
g_hbl_override_config.program_configs[i].override_key = ParseOverrideKey(value);
}
inline void SetHblSpecificAddressSpace(size_t i, const char *value) {
g_hbl_override_config.program_as_flags[i] = ParseOverrideAddressSpace(value);
}
int OverrideConfigIniHandler(void *user, const char *section, const char *name, const char *value) {
AMS_UNUSED(user);
/* Taken and modified, with love, from Rajkosto's implementation. */
if (strcasecmp(section, "hbl_config") == 0) {
if (strcasecmp(name, "program_id") == 0 || strcasecmp(name, "program_id_0") == 0) {
SetHblSpecificProgramId(0, value);
} else if (strcasecmp(name, "program_id_1") == 0) {
SetHblSpecificProgramId(1, value);
} else if (strcasecmp(name, "program_id_2") == 0) {
SetHblSpecificProgramId(2, value);
} else if (strcasecmp(name, "program_id_3") == 0) {
SetHblSpecificProgramId(3, value);
} else if (strcasecmp(name, "program_id_4") == 0) {
SetHblSpecificProgramId(4, value);
} else if (strcasecmp(name, "program_id_5") == 0) {
SetHblSpecificProgramId(5, value);
} else if (strcasecmp(name, "program_id_6") == 0) {
SetHblSpecificProgramId(6, value);
} else if (strcasecmp(name, "program_id_7") == 0) {
SetHblSpecificProgramId(7, value);
} else if (strcasecmp(name, "override_key") == 0 || strcasecmp(name, "override_key_0") == 0) {
SetHblSpecificOverrideKey(0, value);
} else if (strcasecmp(name, "override_key_1") == 0) {
SetHblSpecificOverrideKey(1, value);
} else if (strcasecmp(name, "override_key_2") == 0) {
SetHblSpecificOverrideKey(2, value);
} else if (strcasecmp(name, "override_key_3") == 0) {
SetHblSpecificOverrideKey(3, value);
} else if (strcasecmp(name, "override_key_4") == 0) {
SetHblSpecificOverrideKey(4, value);
} else if (strcasecmp(name, "override_key_5") == 0) {
SetHblSpecificOverrideKey(5, value);
} else if (strcasecmp(name, "override_key_6") == 0) {
SetHblSpecificOverrideKey(6, value);
} else if (strcasecmp(name, "override_key_7") == 0) {
SetHblSpecificOverrideKey(7, value);
} else if (strcasecmp(name, "override_address_space") == 0 || strcasecmp(name, "override_address_space_0") == 0) {
SetHblSpecificAddressSpace(0, value);
} else if (strcasecmp(name, "override_address_space_1") == 0) {
SetHblSpecificAddressSpace(1, value);
} else if (strcasecmp(name, "override_address_space_2") == 0) {
SetHblSpecificAddressSpace(2, value);
} else if (strcasecmp(name, "override_address_space_3") == 0) {
SetHblSpecificAddressSpace(3, value);
} else if (strcasecmp(name, "override_address_space_4") == 0) {
SetHblSpecificAddressSpace(4, value);
} else if (strcasecmp(name, "override_address_space_5") == 0) {
SetHblSpecificAddressSpace(5, value);
} else if (strcasecmp(name, "override_address_space_6") == 0) {
SetHblSpecificAddressSpace(6, value);
} else if (strcasecmp(name, "override_address_space_7") == 0) {
SetHblSpecificAddressSpace(7, value);
} else if (strcasecmp(name, "override_any_app") == 0) {
if (strcasecmp(value, "true") == 0 || strcasecmp(value, "1") == 0) {
g_hbl_override_config.override_any_app = true;
} else if (strcasecmp(value, "false") == 0 || strcasecmp(value, "0") == 0) {
g_hbl_override_config.override_any_app = false;
} else {
/* I guess we default to not changing the value? */
}
} else if (strcasecmp(name, "override_any_app_key") == 0) {
g_hbl_override_config.override_any_app_key = ParseOverrideKey(value);
} else if (strcasecmp(name, "override_any_app_address_space") == 0) {
g_hbl_override_config.override_any_app_as_flag = ParseOverrideAddressSpace(value);
} else if (strcasecmp(name, "path") == 0) {
while (*value == '/' || *value == '\\') {
value++;
}
util::SNPrintf(g_hbl_sd_path, sizeof(g_hbl_sd_path) - 1, "/%s", value);
g_hbl_sd_path[sizeof(g_hbl_sd_path) - 1] = '\0';
for (size_t i = 0; i < sizeof(g_hbl_sd_path); i++) {
if (g_hbl_sd_path[i] == '\\') {
g_hbl_sd_path[i] = '/';
}
}
}
} else if (strcasecmp(section, "default_config") == 0) {
if (strcasecmp(name, "override_key") == 0) {
g_default_override_key = ParseOverrideKey(value);
} else if (strcasecmp(name, "cheat_enable_key") == 0) {
g_default_cheat_enable_key = ParseOverrideKey(value);
}
} else {
return 0;
}
return 1;
}
int ContentSpecificIniHandler(void *user, const char *section, const char *name, const char *value) {
ContentSpecificOverrideConfig *config = reinterpret_cast<ContentSpecificOverrideConfig *>(user);
if (strcasecmp(section, "override_config") == 0) {
if (strcasecmp(name, "override_key") == 0) {
config->override_key = ParseOverrideKey(value);
} else if (strcasecmp(name, "cheat_enable_key") == 0) {
config->cheat_enable_key = ParseOverrideKey(value);
} else if (strcasecmp(name, "override_language") == 0) {
config->locale.language_code = settings::LanguageCode::Encode(value);
} else if (strcasecmp(name, "override_region") == 0) {
if (strcasecmp(value, "jpn") == 0) {
config->locale.region_code = settings::RegionCode_Japan;
} else if (strcasecmp(value, "usa") == 0) {
config->locale.region_code = settings::RegionCode_America;
} else if (strcasecmp(value, "eur") == 0) {
config->locale.region_code = settings::RegionCode_Europe;
} else if (strcasecmp(value, "aus") == 0) {
config->locale.region_code = settings::RegionCode_Australia;
} else if (strcasecmp(value, "chn") == 0) {
config->locale.region_code = settings::RegionCode_China;
} else if (strcasecmp(value, "kor") == 0) {
config->locale.region_code = settings::RegionCode_Korea;
} else if (strcasecmp(value, "twn") == 0) {
config->locale.region_code = settings::RegionCode_Taiwan;
}
}
} else {
return 0;
}
return 1;
}
constexpr inline bool IsOverrideMatch(const OverrideStatus &status, const OverrideKey &cfg) {
bool keys_triggered = ((status.keys_held & cfg.key_combination) != 0);
return (cfg.override_by_default ^ keys_triggered);
}
inline bool IsAnySpecificHblProgramId(ncm::ProgramId program_id) {
for (size_t i = 0; i < MaxProgramOverrideKeys; i++) {
if (program_id == g_hbl_override_config.program_configs[i].program_id) {
return true;
}
}
return false;
}
inline bool IsSpecificHblProgramId(size_t i, ncm::ProgramId program_id) {
return program_id == g_hbl_override_config.program_configs[i].program_id;
}
inline bool IsAnyApplicationHblProgramId(ncm::ProgramId program_id) {
return g_hbl_override_config.override_any_app && ncm::IsApplicationId(program_id) && !IsAnySpecificHblProgramId(program_id);
}
std::atomic<u32> g_ini_mount_count;
void GetIniMountName(char *dst) {
util::SNPrintf(dst, fs::MountNameLengthMax + 1, "#ini%08x", g_ini_mount_count.fetch_add(1));
}
void ParseIniFile(util::ini::Handler handler, const char *path, void *user_ctx) {
/* Mount the SD card. */
char mount_name[fs::MountNameLengthMax + 1];
GetIniMountName(mount_name);
if (R_FAILED(fs::MountSdCard(mount_name))) {
return;
}
ON_SCOPE_EXIT { fs::Unmount(mount_name); };
/* Open the file. */
fs::FileHandle file;
{
char full_path[fs::EntryNameLengthMax + 1];
util::SNPrintf(full_path, sizeof(full_path), "%s:/%s", mount_name, path[0] == '/' ? path + 1 : path);
if (R_FAILED(fs::OpenFile(std::addressof(file), full_path, fs::OpenMode_Read))) {
return;
}
}
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Parse the config. */
util::ini::ParseFile(file, user_ctx, handler);
}
void RefreshOverrideConfiguration() {
ParseIniFile(OverrideConfigIniHandler, "/atmosphere/config/override_config.ini", nullptr);
}
ContentSpecificOverrideConfig GetContentOverrideConfig(ncm::ProgramId program_id) {
char path[fs::EntryNameLengthMax + 1];
util::SNPrintf(path, sizeof(path), "/atmosphere/contents/%016" PRIx64 "/config.ini", program_id.value);
ContentSpecificOverrideConfig config = {
.override_key = g_default_override_key,
.cheat_enable_key = g_default_cheat_enable_key,
};
std::memset(std::addressof(config.locale), 0xCC, sizeof(config.locale));
ParseIniFile(ContentSpecificIniHandler, path, std::addressof(config));
return config;
}
}
OverrideStatus CaptureOverrideStatus(ncm::ProgramId program_id) {
OverrideStatus status = {};
/* If the SD card isn't initialized, we can't override. */
if (!IsSdCardInitialized()) {
return status;
}
/* For system modules and anything launched before the home menu, always override. */
if (program_id < ncm::SystemAppletId::Start || !pm::info::HasLaunchedBootProgram(ncm::SystemAppletId::Qlaunch)) {
status.SetProgramSpecific();
return status;
}
/* Unconditionally refresh override_config.ini contents. */
RefreshOverrideConfiguration();
/* If we can't read the key state, don't override anything. */
if (R_FAILED(hid::GetKeysHeld(std::addressof(status.keys_held)))) {
return status;
}
/* Detect Hbl. */
if (IsAnyApplicationHblProgramId(program_id) && IsOverrideMatch(status, g_hbl_override_config.override_any_app_key)) {
status.SetHbl();
status.flags &= ~impl::OverrideStatusFlag_AddressSpaceMask;
status.flags |= g_hbl_override_config.override_any_app_as_flag;
}
for (size_t i = 0; i < MaxProgramOverrideKeys; i++) {
if (IsSpecificHblProgramId(i, program_id) && IsOverrideMatch(status, g_hbl_override_config.program_configs[i].override_key)) {
status.SetHbl();
status.flags &= ~impl::OverrideStatusFlag_AddressSpaceMask;
status.flags |= g_hbl_override_config.program_as_flags[i];
}
}
/* Detect content specific keys. */
const auto content_cfg = GetContentOverrideConfig(program_id);
if (IsOverrideMatch(status, content_cfg.override_key)) {
status.SetProgramSpecific();
}
/* Only allow cheat enable if not HBL. */
if (!status.IsHbl() && IsOverrideMatch(status, content_cfg.cheat_enable_key)) {
status.SetCheatEnabled();
}
return status;
}
OverrideLocale GetOverrideLocale(ncm::ProgramId program_id) {
return GetContentOverrideConfig(program_id).locale;
}
/* HBL Configuration utilities. */
const char *GetHblPath() {
return g_hbl_sd_path;
}
}

View File

@@ -1,22 +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>
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
#include "cfg_override.board.nintendo_nx.inc"
#else
#include "cfg_override.generic.inc"
#endif

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>
namespace ams::cfg {
OverrideStatus CaptureOverrideStatus(ncm::ProgramId program_id) {
AMS_UNUSED(program_id);
AMS_ABORT("TODO: How should this work?");
}
OverrideLocale GetOverrideLocale(ncm::ProgramId program_id) {
AMS_UNUSED(program_id);
AMS_ABORT("TODO: How should this work?");
}
/* HBL Configuration utilities. */
const char *GetHblPath() {
AMS_ABORT("TODO: How should this work?");
}
}

View File

@@ -1,96 +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::cfg {
namespace {
/* Convenience definitions. */
constexpr sm::ServiceName RequiredServicesForSdCardAccess[] = {
sm::ServiceName::Encode("pcv"),
sm::ServiceName::Encode("gpio"),
sm::ServiceName::Encode("pinmux"),
sm::ServiceName::Encode("psc:m"),
};
constexpr size_t NumRequiredServicesForSdCardAccess = util::size(RequiredServicesForSdCardAccess);
/* SD card globals. */
constinit os::SdkMutex g_sd_card_lock;
constinit bool g_sd_card_initialized = false;
constinit FsFileSystem g_sd_card_filesystem = {};
/* SD card helpers. */
Result CheckSdCardServicesReady() {
for (size_t i = 0; i < NumRequiredServicesForSdCardAccess; i++) {
bool service_present = false;
R_TRY(sm::HasService(std::addressof(service_present), RequiredServicesForSdCardAccess[i]));
if (!service_present) {
return fs::ResultSdCardNotPresent();
}
}
return ResultSuccess();
}
void WaitSdCardServicesReadyImpl() {
for (size_t i = 0; i < NumRequiredServicesForSdCardAccess; i++) {
R_ABORT_UNLESS(sm::WaitService(RequiredServicesForSdCardAccess[i]));
}
}
Result TryInitializeSdCard() {
R_TRY(CheckSdCardServicesReady());
R_ABORT_UNLESS(fsOpenSdCardFileSystem(std::addressof(g_sd_card_filesystem)));
g_sd_card_initialized = true;
return ResultSuccess();
}
void InitializeSdCard() {
WaitSdCardServicesReadyImpl();
R_ABORT_UNLESS(fsOpenSdCardFileSystem(std::addressof(g_sd_card_filesystem)));
g_sd_card_initialized = true;
}
}
/* SD card utilities. */
bool IsSdCardRequiredServicesReady() {
return R_SUCCEEDED(CheckSdCardServicesReady());
}
void WaitSdCardRequiredServicesReady() {
WaitSdCardServicesReadyImpl();
}
bool IsSdCardInitialized() {
std::scoped_lock lk(g_sd_card_lock);
if (!g_sd_card_initialized) {
if (R_SUCCEEDED(TryInitializeSdCard())) {
g_sd_card_initialized = true;
}
}
return g_sd_card_initialized;
}
void WaitSdCardInitialized() {
std::scoped_lock lk(g_sd_card_lock);
InitializeSdCard();
}
}

View File

@@ -1,22 +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>
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
#include "cfg_sd_card.board.nintendo_nx.inc"
#else
#include "cfg_sd_card.generic.inc"
#endif

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 <stratosphere.hpp>
namespace ams::cfg {
bool IsSdCardRequiredServicesReady() {
return true;
}
void WaitSdCardRequiredServicesReady() {
/* ... */
}
bool IsSdCardInitialized() {
return true;
}
void WaitSdCardInitialized() {
/* ... */
}
}

View File

@@ -1,59 +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 "../spl/impl/spl_ctr_drbg.hpp"
#include <sys/random.h>
namespace ams::crypto {
namespace {
using Drbg = ::ams::spl::impl::CtrDrbg<crypto::AesEncryptor128, crypto::AesEncryptor128::KeySize, false>;
constinit util::TypedStorage<Drbg> g_drbg = {};
bool InitializeCsrng() {
u8 seed[Drbg::SeedSize];
AMS_ABORT_UNLESS(::getentropy(seed, sizeof(seed)) == 0);
util::ConstructAt(g_drbg);
util::GetReference(g_drbg).Initialize(seed, sizeof(seed), nullptr, 0, nullptr, 0);
return true;
}
}
void GenerateCryptographicallyRandomBytes(void *dst, size_t dst_size) {
AMS_FUNCTION_LOCAL_STATIC(bool, s_initialized, InitializeCsrng());
AMS_ABORT_UNLESS(s_initialized);
AMS_ASSERT(dst_size <= Drbg::RequestSizeMax);
if (!util::GetReference(g_drbg).Generate(dst, dst_size, nullptr, 0)) {
/* Reseed, if needed. */
{
u8 seed[Drbg::SeedSize];
AMS_ABORT_UNLESS(::getentropy(seed, sizeof(seed)) == 0);
util::GetReference(g_drbg).Reseed(seed, sizeof(seed), nullptr, 0);
}
util::GetReference(g_drbg).Generate(dst, dst_size, nullptr, 0);
}
}
}

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/>.
*/
#include <stratosphere.hpp>
namespace ams::crypto {
namespace {
constinit bool g_initialized = false;
constinit os::SdkMutex g_lock;
void InitializeCsrng() {
AMS_ASSERT(!g_initialized);
R_ABORT_UNLESS(sm::Initialize());
R_ABORT_UNLESS(::csrngInitialize());
}
}
void GenerateCryptographicallyRandomBytes(void *dst, size_t dst_size) {
if (AMS_UNLIKELY(!g_initialized)) {
std::scoped_lock lk(g_lock);
if (AMS_LIKELY(!g_initialized)) {
InitializeCsrng();
g_initialized = true;
}
}
R_ABORT_UNLESS(::csrngGetRandomBytes(dst, dst_size));
}
}

View File

@@ -1,28 +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 <stratosphere/windows.hpp>
#include <ntstatus.h>
#include <bcrypt.h>
namespace ams::crypto {
void GenerateCryptographicallyRandomBytes(void *dst, size_t dst_size) {
const auto status = ::BCryptGenRandom(nullptr, static_cast<PUCHAR>(dst), dst_size, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
AMS_ABORT_UNLESS(status == STATUS_SUCCESS);
}
}

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/>.
*/
#include <stratosphere.hpp>
namespace ams::cs {
void InitializeAudioServer() {
/* TODO: Support audio server. */
}
}

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::cs {
struct CommandDataTakeScreenShot {
vi::LayerStack layer_stack;
u8 *buffer;
size_t buffer_size;
};
Result DoGetFirmwareVersionCommand(settings::system::FirmwareVersion *out);
template<typename SendHeader, typename SendData>
Result DoTakeScreenShotCommand(const CommandDataTakeScreenShot &params, SendHeader send_header, SendData send_data);
}

View File

@@ -1,82 +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/>.
*/
namespace ams::cs {
namespace {
template<typename SendData>
void SendEmptyData(const CommandDataTakeScreenShot &params, size_t remaining_size, SendData send_data) {
/* Clear the data buffer. */
std::memset(params.buffer, 0, params.buffer_size);
/* Send data until the end. */
while (remaining_size > 0) {
/* Send as much as we can. */
const auto cur_size = std::min(remaining_size, params.buffer_size);
send_data(params.buffer, cur_size);
/* Advance. */
remaining_size -= cur_size;
}
}
}
Result DoGetFirmwareVersionCommand(settings::system::FirmwareVersion *out) {
settings::system::GetFirmwareVersion(out);
return ResultSuccess();
}
template<typename SendHeader, typename SendData>
Result DoTakeScreenShotCommand(const CommandDataTakeScreenShot &params, SendHeader send_header, SendData send_data) {
/* Initialize screenshot control. */
R_TRY(capsrv::InitializeScreenShotControl());
/* Finalize screenshot control when we're done. */
ON_SCOPE_EXIT { capsrv::FinalizeScreenShotControl(); };
/* Open screenshot read stream. */
size_t data_size;
s32 width, height;
R_TRY(capsrv::OpenRawScreenShotReadStreamForDevelop(std::addressof(data_size), std::addressof(width), std::addressof(height), params.layer_stack, TimeSpan::FromSeconds(10)));
/* Close the screenshot stream when we're done. */
ON_SCOPE_EXIT { capsrv::CloseRawScreenShotReadStreamForDevelop(); };
/* Send the header. */
send_header(static_cast<s32>(data_size), width, height);
/* Read and send data. */
size_t total_read_size = 0;
auto data_guard = SCOPE_GUARD { SendEmptyData(params, data_size - total_read_size, send_data); };
while (total_read_size < data_size) {
/* Read data from the stream. */
size_t read_size;
R_TRY(capsrv::ReadRawScreenShotReadStreamForDevelop(std::addressof(read_size), params.buffer, params.buffer_size, total_read_size));
/* Send the data that was read. */
send_data(params.buffer, read_size);
/* Advance. */
total_read_size += read_size;
}
data_guard.Cancel();
return ResultSuccess();
}
}

View File

@@ -1,129 +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 "cs_command_impl.hpp"
/* Include command implementations. */
#include "cs_command_impl.inc"
namespace ams::cs {
namespace {
struct ResponseFirmwareVersion {
ResponseHeader header;
settings::system::FirmwareVersion firmware_version;
};
struct ResponseTakeScreenShot {
ResponseHeader header;
s32 data_size;
s32 width;
s32 height;
};
constinit u8 g_data[0x1000];
}
bool CommandProcessor::ProcessCommand(const CommandHeader &header, const u8 *body, s32 socket) {
switch (header.command) {
case Command_GetFirmwareVersion:
SendFirmwareVersion(socket, header);
break;
case Command_TakeScreenShot:
this->TakeScreenShot(header, socket, vi::LayerStack_ApplicationForDebug);
break;
case Command_TakeForegroundScreenShot:
this->TakeScreenShot(header, socket, vi::LayerStack_LastFrame);
break;
/* TODO: Command support. */
default:
scs::CommandProcessor::ProcessCommand(header, body, socket);
break;
}
return true;
}
void CommandProcessor::SendFirmwareVersion(s32 socket, const CommandHeader &header) {
/* Build the response. */
ResponseFirmwareVersion response = {
.header = {
.id = header.id,
.response = Response_FirmwareVersion,
.body_size = sizeof(response) - sizeof(response.header),
},
.firmware_version = {},
};
/* Get the firmware version. */
const Result result = DoGetFirmwareVersionCommand(std::addressof(response.firmware_version));
if (R_SUCCEEDED(result)) {
/* Send the response. */
auto lk = MakeSendGuardBlock();
Send(socket, std::addressof(response), sizeof(response));
} else {
SendErrorResult(socket, header, result);
}
}
void CommandProcessor::TakeScreenShot(const CommandHeader &header, s32 socket, vi::LayerStack layer_stack) {
/* Create the command data. */
const CommandDataTakeScreenShot params = {
.layer_stack = layer_stack,
.buffer = g_data,
.buffer_size = sizeof(g_data),
};
/* Take the screenshot. */
Result result;
{
/* Acquire the send lock. */
auto lk = MakeSendGuardBlock();
/* Perform the command. */
result = DoTakeScreenShotCommand(params, [&](s32 data_size, s32 width, s32 height) {
/* Use global buffer for response. */
ResponseTakeScreenShot *response = reinterpret_cast<ResponseTakeScreenShot *>(g_data);
/* Set response header. */
*response = {
.header = {
.id = header.id,
.response = Response_ScreenShot,
.body_size = static_cast<u32>(sizeof(data_size) + sizeof(width) + sizeof(height) + data_size),
},
.data_size = data_size,
.width = width,
.height = height,
};
/* Send data. */
Send(socket, response, sizeof(*response));
}, [&](u8 *data, size_t data_size) {
/* Send data. */
Send(socket, data, data_size);
});
}
/* Handle the error case. */
if (R_FAILED(result)) {
SendErrorResult(socket, header, result);
}
}
}

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/>.
*/
#include <stratosphere.hpp>
namespace ams::cs {
void InitializeHidServer() {
/* TODO: Support hid redirection server. */
}
}

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/>.
*/
#include <stratosphere.hpp>
namespace ams::cs {
void InitializeRemoteVideoServer() {
/* TODO: Support remote video server. */
}
}

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/>.
*/
#include <stratosphere.hpp>
namespace ams::cs {
void InitializeTargetIoServer() {
/* Launch target io server. */
os::ProcessId process_id;
scs::LaunchProgram(std::addressof(process_id), ncm::ProgramLocation::Make(ncm::SystemProgramId::DevServer, ncm::StorageId::None), nullptr, 0, 0);
}
}

View File

@@ -1,132 +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 "impl/dd_device_address_space_impl.hpp"
namespace ams::dd {
Result CreateDeviceAddressSpace(DeviceAddressSpaceType *das, u64 address, u64 size) {
/* Check pre-conditions. */
AMS_ASSERT(util::IsAligned(address, os::MemoryPageSize));
AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize));
AMS_ASSERT(size > 0);
/* Ensure we leave in a consistent state. */
auto state_guard = SCOPE_GUARD { das->state = DeviceAddressSpaceType::State_NotInitialized; };
/* Create the address space. */
DeviceAddressSpaceHandle handle;
R_TRY(impl::DeviceAddressSpaceImpl::Create(std::addressof(handle), address, size));
/* Set the values in the das. */
das->device_handle = handle;
das->is_handle_managed = true;
das->state = DeviceAddressSpaceType::State_Initialized;
/* We succeeded. */
state_guard.Cancel();
R_SUCCEED();
}
Result CreateDeviceAddressSpace(DeviceAddressSpaceType *das, u64 size) {
R_RETURN(CreateDeviceAddressSpace(das, 0, size));
}
void DestroyDeviceAddressSpace(DeviceAddressSpaceType *das) {
/* Check pre-conditions. */
AMS_ASSERT(das->state == DeviceAddressSpaceType::State_Initialized);
/* Destroy the handle. */
if (das->is_handle_managed) {
impl::DeviceAddressSpaceImpl::Close(das->device_handle);
}
das->device_handle = 0;
das->is_handle_managed = false;
das->state = DeviceAddressSpaceType::State_NotInitialized;
}
void AttachDeviceAddressSpaceHandle(DeviceAddressSpaceType *das, DeviceAddressSpaceHandle handle, bool managed) {
/* Check pre-conditions. */
AMS_ASSERT(handle != os::InvalidNativeHandle);
das->device_handle = handle;
das->is_handle_managed = managed;
das->state = DeviceAddressSpaceType::State_Initialized;
}
DeviceAddressSpaceHandle GetDeviceAddressSpaceHandle(DeviceAddressSpaceType *das) {
/* Check pre-conditions. */
AMS_ASSERT(das->state == DeviceAddressSpaceType::State_Initialized);
return das->device_handle;
}
Result MapDeviceAddressSpaceAligned(DeviceAddressSpaceType *das, ProcessHandle process_handle, u64 process_address, size_t size, DeviceVirtualAddress device_address, MemoryPermission device_perm) {
/* Check pre-conditions. */
AMS_ASSERT(das->state == DeviceAddressSpaceType::State_Initialized);
AMS_ASSERT(util::IsAligned(process_address, os::MemoryPageSize));
AMS_ASSERT(util::IsAligned(device_address, os::MemoryPageSize));
AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize));
AMS_ASSERT(process_address + size > process_address);
AMS_ASSERT(device_address + size > device_address);
AMS_ASSERT(size > 0);
AMS_ASSERT((process_address & (4_MB - 1)) == (device_address & (4_MB - 1)));
R_RETURN(impl::DeviceAddressSpaceImpl::MapAligned(das->device_handle, process_handle, process_address, size, device_address, device_perm));
}
Result MapDeviceAddressSpaceNotAligned(DeviceAddressSpaceType *das, ProcessHandle process_handle, u64 process_address, size_t size, DeviceVirtualAddress device_address, MemoryPermission device_perm) {
/* Check pre-conditions. */
AMS_ASSERT(das->state == DeviceAddressSpaceType::State_Initialized);
AMS_ASSERT(util::IsAligned(process_address, os::MemoryPageSize));
AMS_ASSERT(util::IsAligned(device_address, os::MemoryPageSize));
AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize));
AMS_ASSERT(process_address + size > process_address);
AMS_ASSERT(device_address + size > device_address);
AMS_ASSERT(size > 0);
R_RETURN(impl::DeviceAddressSpaceImpl::MapNotAligned(das->device_handle, process_handle, process_address, size, device_address, device_perm));
}
void UnmapDeviceAddressSpace(DeviceAddressSpaceType *das, ProcessHandle process_handle, u64 process_address, size_t size, DeviceVirtualAddress device_address) {
/* Check pre-conditions. */
AMS_ASSERT(das->state == DeviceAddressSpaceType::State_Initialized);
AMS_ASSERT(util::IsAligned(process_address, os::MemoryPageSize));
AMS_ASSERT(util::IsAligned(device_address, os::MemoryPageSize));
AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize));
AMS_ASSERT(process_address + size > process_address);
AMS_ASSERT(device_address + size > device_address);
AMS_ASSERT(size > 0);
return impl::DeviceAddressSpaceImpl::Unmap(das->device_handle, process_handle, process_address, size, device_address);
}
Result AttachDeviceAddressSpace(DeviceAddressSpaceType *das, DeviceName device_name) {
/* Check pre-conditions. */
AMS_ASSERT(das->state == DeviceAddressSpaceType::State_Initialized);
R_RETURN(impl::DeviceAddressSpaceImpl::Attach(das, device_name));
}
void DetachDeviceAddressSpace(DeviceAddressSpaceType *das, DeviceName device_name) {
/* Check pre-conditions. */
AMS_ASSERT(das->state == DeviceAddressSpaceType::State_Initialized);
return impl::DeviceAddressSpaceImpl::Detach(das, device_name);
}
}

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>
namespace ams::dd::impl {
class DeviceAddressSpaceImplByWindows {
public:
static Result Create(DeviceAddressSpaceHandle *, u64, u64) {
R_THROW(dd::ResultNotSupported());
}
static void Close(DeviceAddressSpaceHandle) {
/* ... */
}
static Result MapAligned(DeviceAddressSpaceHandle, ProcessHandle, u64, size_t, DeviceVirtualAddress, dd::MemoryPermission) {
R_THROW(dd::ResultNotSupported());
}
static Result MapNotAligned(DeviceAddressSpaceHandle, ProcessHandle, u64, size_t, DeviceVirtualAddress, dd::MemoryPermission) {
R_THROW(dd::ResultNotSupported());
}
static void Unmap(DeviceAddressSpaceHandle, ProcessHandle, u64, size_t, DeviceVirtualAddress) {
/* ... */
}
static Result Attach(DeviceAddressSpaceType *, DeviceName) {
R_THROW(dd::ResultNotSupported());
}
static void Detach(DeviceAddressSpaceType *, DeviceName) {
/* ... */
}
};
using DeviceAddressSpaceImpl = DeviceAddressSpaceImplByWindows;
}

View File

@@ -1,23 +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>
#if defined(ATMOSPHERE_OS_HORIZON)
#include "dd_device_address_space_impl.os.horizon.hpp"
#else
#include "dd_device_address_space_impl.generic.hpp"
#endif

View File

@@ -1,88 +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 "dd_device_address_space_impl.os.horizon.hpp"
namespace ams::dd::impl {
static_assert(static_cast<int>(dd::MemoryPermission_None) == static_cast<int>(svc::MemoryPermission_None));
static_assert(static_cast<int>(dd::MemoryPermission_ReadOnly) == static_cast<int>(svc::MemoryPermission_Read));
static_assert(static_cast<int>(dd::MemoryPermission_WriteOnly) == static_cast<int>(svc::MemoryPermission_Write));
static_assert(static_cast<int>(dd::MemoryPermission_ReadWrite) == static_cast<int>(svc::MemoryPermission_ReadWrite));
Result DeviceAddressSpaceImplByHorizon::Create(DeviceAddressSpaceHandle *out, u64 address, u64 size) {
/* Create the space. */
svc::Handle handle;
R_TRY_CATCH(svc::CreateDeviceAddressSpace(std::addressof(handle), address, size)) {
R_CONVERT(svc::ResultOutOfMemory, dd::ResultOutOfMemory())
R_CONVERT(svc::ResultOutOfResource, dd::ResultOutOfResource())
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
*out = static_cast<DeviceAddressSpaceHandle>(handle);
R_SUCCEED();
}
void DeviceAddressSpaceImplByHorizon::Close(DeviceAddressSpaceHandle handle) {
const auto svc_handle = svc::Handle(handle);
if (svc_handle == svc::PseudoHandle::CurrentThread || svc_handle == svc::PseudoHandle::CurrentProcess) {
return;
}
R_ABORT_UNLESS(svc::CloseHandle(svc_handle));
}
Result DeviceAddressSpaceImplByHorizon::MapAligned(DeviceAddressSpaceHandle handle, ProcessHandle process_handle, u64 process_address, size_t process_size, DeviceVirtualAddress device_address, dd::MemoryPermission device_perm) {
/* Check alignment. */
AMS_ABORT_UNLESS((process_address & (4_MB - 1)) == (device_address & (4_MB - 1)));
R_TRY_CATCH(svc::MapDeviceAddressSpaceAligned(svc::Handle(handle), svc::Handle(process_handle), process_address, process_size, device_address, svc::MapDeviceAddressSpaceOption::Encode(static_cast<svc::MemoryPermission>(device_perm), svc::MapDeviceAddressSpaceFlag_None))) {
R_CONVERT(svc::ResultInvalidHandle, dd::ResultInvalidHandle())
R_CONVERT(svc::ResultOutOfMemory, dd::ResultOutOfMemory())
R_CONVERT(svc::ResultOutOfResource, dd::ResultOutOfResource())
R_CONVERT(svc::ResultInvalidCurrentMemory, dd::ResultInvalidMemoryState())
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
R_SUCCEED();
}
Result DeviceAddressSpaceImplByHorizon::MapNotAligned(DeviceAddressSpaceHandle handle, ProcessHandle process_handle, u64 process_address, size_t process_size, DeviceVirtualAddress device_address, dd::MemoryPermission device_perm) {
R_TRY_CATCH(svc::MapDeviceAddressSpaceByForce(svc::Handle(handle), svc::Handle(process_handle), process_address, process_size, device_address, svc::MapDeviceAddressSpaceOption::Encode(static_cast<svc::MemoryPermission>(device_perm), svc::MapDeviceAddressSpaceFlag_None))) {
R_CONVERT(svc::ResultInvalidHandle, dd::ResultInvalidHandle())
R_CONVERT(svc::ResultOutOfMemory, dd::ResultOutOfMemory())
R_CONVERT(svc::ResultOutOfResource, dd::ResultOutOfResource())
R_CONVERT(svc::ResultInvalidCurrentMemory, dd::ResultInvalidMemoryState())
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
R_SUCCEED();
}
void DeviceAddressSpaceImplByHorizon::Unmap(DeviceAddressSpaceHandle handle, ProcessHandle process_handle, u64 process_address, size_t process_size, DeviceVirtualAddress device_address) {
R_ABORT_UNLESS(svc::UnmapDeviceAddressSpace(svc::Handle(handle), svc::Handle(process_handle), process_address, process_size, device_address));
}
Result DeviceAddressSpaceImplByHorizon::Attach(DeviceAddressSpaceType *das, DeviceName device_name) {
R_TRY_CATCH(svc::AttachDeviceAddressSpace(static_cast<svc::DeviceName>(device_name), svc::Handle(das->device_handle))) {
R_CONVERT(svc::ResultOutOfMemory, dd::ResultOutOfMemory())
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
R_SUCCEED();
}
void DeviceAddressSpaceImplByHorizon::Detach(DeviceAddressSpaceType *das, DeviceName device_name) {
R_ABORT_UNLESS(svc::DetachDeviceAddressSpace(static_cast<svc::DeviceName>(device_name), svc::Handle(das->device_handle)));
}
}

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/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::dd::impl {
class DeviceAddressSpaceImplByHorizon {
public:
static Result Create(DeviceAddressSpaceHandle *out, u64 address, u64 size);
static void Close(DeviceAddressSpaceHandle handle);
static Result MapAligned(DeviceAddressSpaceHandle handle, ProcessHandle process_handle, u64 process_address, size_t process_size, DeviceVirtualAddress device_address, dd::MemoryPermission device_perm);
static Result MapNotAligned(DeviceAddressSpaceHandle handle, ProcessHandle process_handle, u64 process_address, size_t process_size, DeviceVirtualAddress device_address, dd::MemoryPermission device_perm);
static void Unmap(DeviceAddressSpaceHandle handle, ProcessHandle process_handle, u64 process_address, size_t process_size, DeviceVirtualAddress device_address);
static Result Attach(DeviceAddressSpaceType *das, DeviceName device_name);
static void Detach(DeviceAddressSpaceType *das, DeviceName device_name);
};
using DeviceAddressSpaceImpl = DeviceAddressSpaceImplByHorizon;
}

View File

@@ -1,150 +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::ddsf {
Result DeviceCodeEntryManager::Add(DeviceCode device_code, IDevice *device) {
/* Check pre-conditions. */
AMS_ASSERT(device != nullptr);
AMS_ASSERT(device->IsDriverAttached());
/* Acquire exclusive access to the manager. */
std::scoped_lock lk(m_entry_list_lock);
/* Check that we don't already have an entry with the code. */
for (const auto &holder : m_entry_list) {
AMS_ASSERT(holder.IsConstructed());
AMS_ASSERT(holder.Get().GetDeviceCode() != device_code);
AMS_UNUSED(holder);
}
/* Allocate memory for a new device code entry holder. */
void *holder_storage = m_memory_resource->Allocate(sizeof(DeviceCodeEntryHolder));
R_UNLESS(holder_storage != nullptr, ddsf::ResultOutOfResource());
/* Initialize the new holder. */
auto *holder = std::construct_at(static_cast<DeviceCodeEntryHolder *>(holder_storage));
holder->Construct(device_code, device);
/* Link the new holder. */
holder->AddTo(m_entry_list);
R_SUCCEED();
}
bool DeviceCodeEntryManager::Remove(DeviceCode device_code) {
/* Acquire exclusive access to the manager. */
std::scoped_lock lk(m_entry_list_lock);
/* Find and erase the entry. */
bool erased = false;
for (auto it = m_entry_list.begin(); it != m_entry_list.end(); /* ... */) {
/* Get the current entry, and advance the iterator. */
DeviceCodeEntryHolder *cur = std::addressof(*(it++));
/* If the entry matches the device code, remove it. */
AMS_ASSERT(cur->IsConstructed());
if (cur->Get().GetDeviceCode() == device_code) {
/* Destroy and deallocate the holder. */
cur->Destroy();
std::destroy_at(cur);
m_memory_resource->Deallocate(cur, sizeof(*cur));
erased = true;
}
}
return erased;
}
Result DeviceCodeEntryManager::FindDeviceCodeEntry(DeviceCodeEntry **out, DeviceCode device_code) {
/* Check arguments. */
AMS_ASSERT(out != nullptr);
R_UNLESS(out != nullptr, ddsf::ResultInvalidArgument());
/* Find the device. */
bool found = false;
this->ForEachEntry([&](DeviceCodeEntry &entry) -> bool {
if (entry.GetDeviceCode() == device_code) {
found = true;
*out = std::addressof(entry);
return false;
}
return true;
});
/* Check that we found the device. */
R_UNLESS(found, ddsf::ResultDeviceCodeNotFound());
R_SUCCEED();
}
Result DeviceCodeEntryManager::FindDeviceCodeEntry(const DeviceCodeEntry **out, DeviceCode device_code) const {
/* Check arguments. */
AMS_ASSERT(out != nullptr);
R_UNLESS(out != nullptr, ddsf::ResultInvalidArgument());
/* Find the device. */
bool found = false;
this->ForEachEntry([&](const DeviceCodeEntry &entry) -> bool {
if (entry.GetDeviceCode() == device_code) {
found = true;
*out = std::addressof(entry);
return false;
}
return true;
});
/* Check that we found the device. */
R_UNLESS(found, ddsf::ResultDeviceCodeNotFound());
R_SUCCEED();
}
Result DeviceCodeEntryManager::FindDevice(IDevice **out, DeviceCode device_code) {
/* Check pre-conditions. */
AMS_ASSERT(out != nullptr);
/* Find the entry. */
DeviceCodeEntry *entry;
R_TRY(this->FindDeviceCodeEntry(std::addressof(entry), device_code));
/* Set the output. */
if (out != nullptr) {
*out = std::addressof(entry->GetDevice());
}
R_SUCCEED();
}
Result DeviceCodeEntryManager::FindDevice(const IDevice **out, DeviceCode device_code) const {
/* Check pre-conditions. */
AMS_ASSERT(out != nullptr);
/* Find the entry. */
const DeviceCodeEntry *entry;
R_TRY(this->FindDeviceCodeEntry(std::addressof(entry), device_code));
/* Set the output. */
if (out != nullptr) {
*out = std::addressof(entry->GetDevice());
}
R_SUCCEED();
}
}

View File

@@ -1,214 +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::ddsf {
namespace {
enum class LoopControlCommand {
None = 0,
Register = 1,
Unregister = 2,
Terminate = 3,
};
}
struct EventHandlerManager::LoopControlCommandParameters {
LoopControlCommand command;
IEventHandler *target;
LoopControlCommandParameters() : command(LoopControlCommand::None), target(nullptr) { /* ... */ }
LoopControlCommandParameters(LoopControlCommand c, IEventHandler *t) : command(c), target(t) { /* ... */ }
};
void EventHandlerManager::Initialize() {
/* Check that we're not already initialized. */
if (m_is_initialized) {
return;
}
/* Initialize multi wait/holder. */
os::InitializeMultiWait(std::addressof(m_multi_wait));
os::InitializeMultiWaitHolder(std::addressof(m_loop_control_event_holder), m_loop_control_event.GetBase());
os::LinkMultiWaitHolder(std::addressof(m_multi_wait), std::addressof(m_loop_control_event_holder));
m_is_initialized = true;
}
void EventHandlerManager::Finalize() {
/* Check that we're initialized and not looping. */
AMS_ASSERT(!m_is_looping);
AMS_ASSERT(m_is_initialized);
if (!m_is_initialized) {
return;
}
/* Finalize multi wait/holder. */
os::UnlinkMultiWaitHolder(std::addressof(m_loop_control_event_holder));
os::FinalizeMultiWaitHolder(std::addressof(m_loop_control_event_holder));
os::FinalizeMultiWait(std::addressof(m_multi_wait));
m_is_initialized = false;
}
void EventHandlerManager::ProcessControlCommand(LoopControlCommandParameters *params) {
/* Check pre-conditions. */
AMS_ASSERT(m_is_initialized);
AMS_ASSERT(params != nullptr);
/* Acquire exclusive access. */
std::scoped_lock lk(m_loop_control_lock);
/* If we're processing for the loop thread, we can directly handle. */
if (!m_is_looping || this->IsRunningOnLoopThread()) {
this->ProcessControlCommandImpl(params);
} else {
/* Otherwise, signal to the loop thread. */
m_loop_control_command_params = params;
m_loop_control_event.Signal();
m_loop_control_command_done_event.Wait();
}
}
void EventHandlerManager::ProcessControlCommandImpl(LoopControlCommandParameters *params) {
/* Check pre-conditions. */
AMS_ASSERT(m_loop_control_lock.IsLockedByCurrentThread() || !m_loop_control_lock.TryLock());
AMS_ASSERT(params != nullptr);
AMS_ASSERT(params->target != nullptr);
/* Process the command. */
switch (params->command) {
case LoopControlCommand::Register:
params->target->Link(std::addressof(m_multi_wait));
break;
case LoopControlCommand::Unregister:
params->target->Unlink();
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
void EventHandlerManager::RegisterHandler(IEventHandler *handler) {
/* Check that the handler is valid. */
AMS_ASSERT(handler != nullptr);
AMS_ASSERT(handler->IsInitialized());
/* Send registration command. */
LoopControlCommandParameters params(LoopControlCommand::Register, handler);
return this->ProcessControlCommand(std::addressof(params));
}
void EventHandlerManager::UnregisterHandler(IEventHandler *handler) {
/* Check that the handler is valid. */
AMS_ASSERT(handler != nullptr);
AMS_ASSERT(handler->IsInitialized());
/* Send registration command. */
LoopControlCommandParameters params(LoopControlCommand::Unregister, handler);
return this->ProcessControlCommand(std::addressof(params));
}
void EventHandlerManager::WaitLoopEnter() {
/* Acquire exclusive access. */
std::scoped_lock lk(m_loop_control_lock);
/* Wait until we're looping. */
while (!m_is_looping) {
m_is_looping_cv.Wait(m_loop_control_lock);
}
}
void EventHandlerManager::WaitLoopExit() {
/* Acquire exclusive access. */
std::scoped_lock lk(m_loop_control_lock);
/* Wait until we're not looping. */
while (m_is_looping) {
m_is_looping_cv.Wait(m_loop_control_lock);
}
}
void EventHandlerManager::RequestStop() {
/* Check that we're looping and not the loop thread. */
AMS_ASSERT(m_is_looping);
AMS_ASSERT(!this->IsRunningOnLoopThread());
if (m_is_looping) {
/* Acquire exclusive access. */
std::scoped_lock lk(m_loop_control_lock);
/* Signal to the loop thread. */
LoopControlCommandParameters params(LoopControlCommand::Terminate, nullptr);
m_loop_control_command_params = std::addressof(params);
m_loop_control_event.Signal();
m_loop_control_command_done_event.Wait();
}
}
void EventHandlerManager::LoopAuto() {
/* Check that we're not already looping. */
AMS_ASSERT(!m_is_looping);
/* Begin looping with the current thread. */
m_loop_thread = os::GetCurrentThread();
m_is_looping = true;
m_is_looping_cv.Broadcast();
/* Whenever we're done looping, clean up. */
ON_SCOPE_EXIT {
m_loop_thread = nullptr;
m_is_looping = false;
m_is_looping_cv.Broadcast();
};
/* Loop until we're asked to stop. */
bool should_terminate = false;
while (!should_terminate) {
/* Wait for a holder to be signaled. */
os::MultiWaitHolderType *event_holder = os::WaitAny(std::addressof(m_multi_wait));
AMS_ASSERT(event_holder != nullptr);
/* Check if we have a request to handle. */
if (event_holder == std::addressof(m_loop_control_event_holder)) {
/* Check that the request hasn't already been handled. */
if (m_loop_control_event.TryWait()) {
/* Handle the request. */
AMS_ASSERT(m_loop_control_command_params != nullptr);
switch (m_loop_control_command_params->command) {
case LoopControlCommand::Register:
case LoopControlCommand::Unregister:
this->ProcessControlCommandImpl(m_loop_control_command_params);
break;
case LoopControlCommand::Terminate:
should_terminate = true;
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Clear the request, and signal that it's done. */
m_loop_control_command_params = nullptr;
m_loop_control_command_done_event.Signal();
}
} else {
/* Handle the event. */
IEventHandler::ToEventHandler(event_holder).HandleEvent();
}
}
}
}

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>
namespace ams::ddsf {
namespace {
constinit ams::MemoryResource *g_memory_resource = nullptr;
constinit ams::MemoryResource *g_device_code_entry_holder_memory_resource = nullptr;
}
void SetMemoryResource(ams::MemoryResource *mr) {
AMS_ASSERT(g_memory_resource == nullptr);
g_memory_resource = mr;
AMS_ASSERT(g_memory_resource != nullptr);
}
ams::MemoryResource *GetMemoryResource() {
AMS_ASSERT(g_memory_resource != nullptr);
return g_memory_resource;
}
void SetDeviceCodeEntryHolderMemoryResource(ams::MemoryResource *mr) {
AMS_ASSERT(g_device_code_entry_holder_memory_resource == nullptr);
g_device_code_entry_holder_memory_resource = mr;
AMS_ASSERT(g_device_code_entry_holder_memory_resource != nullptr);
}
ams::MemoryResource *GetDeviceCodeEntryHolderMemoryResource() {
AMS_ASSERT(g_device_code_entry_holder_memory_resource != nullptr);
return g_device_code_entry_holder_memory_resource;
}
}

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>
namespace ams::ddsf {
Result OpenSession(IDevice *device, ISession *session, AccessMode access_mode) {
/* Check pre-conditions. */
AMS_ASSERT(device != nullptr);
AMS_ASSERT(session != nullptr);
AMS_ASSERT(!session->IsOpen());
/* Attack the session to the device. */
session->AttachDevice(device, access_mode);
auto session_guard = SCOPE_GUARD { session->DetachDevice(); };
/* Attach the device to the session. */
R_TRY(device->AttachSession(session));
/* We succeeded. */
session_guard.Cancel();
R_SUCCEED();
}
void CloseSession(ISession *session) {
/* Check pre-conditions. */
AMS_ASSERT(session != nullptr);
/* Detach the device from the session. */
session->GetDevice().DetachSession(session);
/* Detach the session from the device. */
session->DetachDevice();
}
}

View File

@@ -1,66 +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 "impl/diag_abort_observer_manager.hpp"
namespace ams::diag {
namespace impl {
constinit bool g_enable_default_abort_observer = true;
}
namespace {
template<typename Holder, typename Observer>
void InitializeAbortObserverHolderImpl(Holder *holder, Observer observer) {
holder->observer = observer;
holder->next = nullptr;
holder->is_registered = false;
}
}
void InitializeAbortObserverHolder(AbortObserverHolder *holder, AbortObserver observer) {
InitializeAbortObserverHolderImpl(holder, observer);
}
void RegisterAbortObserver(AbortObserverHolder *holder) {
impl::GetAbortObserverManager()->RegisterObserver(holder);
}
void UnregisterAbortObserver(AbortObserverHolder *holder) {
impl::GetAbortObserverManager()->UnregisterObserver(holder);
}
void EnableDefaultAbortObserver(bool en) {
::ams::diag::impl::g_enable_default_abort_observer = en;
}
void InitializeSdkAbortObserverHolder(SdkAbortObserverHolder *holder, SdkAbortObserver observer) {
InitializeAbortObserverHolderImpl(holder, observer);
}
void RegisterSdkAbortObserver(SdkAbortObserverHolder *holder) {
impl::GetSdkAbortObserverManager()->RegisterObserver(holder);
}
void UnregisterSdkAbortObserver(SdkAbortObserverHolder *holder) {
impl::GetSdkAbortObserverManager()->UnregisterObserver(holder);
}
}

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 "impl/diag_get_all_backtrace.hpp"
#include "impl/diag_invoke_abort.hpp"
namespace ams::diag {
namespace {
inline NORETURN void AbortWithValue(u64 debug) {
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
/* Just perform a data abort. */
register u64 addr __asm__("x27") = FatalErrorContext::StdAbortMagicAddress;
register u64 val __asm__("x28") = FatalErrorContext::StdAbortMagicValue;
while (true) {
__asm__ __volatile__ (
"mov x0, %[debug]\n"
"str %[val], [%[addr]]\n"
:
: [debug]"r"(debug), [val]"r"(val), [addr]"r"(addr)
: "x0"
);
}
#else
AMS_UNUSED(debug);
std::abort();
#endif
__builtin_unreachable();
}
constinit os::SdkMutex g_assert_mutex;
constinit os::SdkMutex g_abort_mutex;
void PrepareAbort() {
#if defined(ATMOSPHERE_OS_HORIZON)
{
/* Get the thread local region. */
auto * const tlr = svc::GetThreadLocalRegion();
/* Clear disable count. */
tlr->disable_count = 0;
/* If we need to, unpin. */
if (tlr->interrupt_flag) {
svc::SynchronizePreemptionState();
}
}
#endif
}
AbortReason ToAbortReason(AssertionType type) {
switch (type) {
case AssertionType_Audit: return AbortReason_Audit;
case AssertionType_Assert: return AbortReason_Assert;
default:
return AbortReason_Abort;
}
}
AssertionFailureOperation DefaultAssertionFailureHandler(const AssertionInfo &) {
return AssertionFailureOperation_Abort;
}
constinit AssertionFailureHandler g_assertion_failure_handler = &DefaultAssertionFailureHandler;
void ExecuteAssertionFailureOperation(AssertionFailureOperation operation, const AssertionInfo &info) {
switch (operation) {
case AssertionFailureOperation_Continue:
break;
case AssertionFailureOperation_Abort:
{
const AbortInfo abort_info = {
ToAbortReason(info.type),
info.message,
info.expr,
info.func,
info.file,
info.line,
};
::ams::diag::impl::InvokeAbortObserver(abort_info);
AbortWithValue(0);
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
void InvokeAssertionFailureHandler(const AssertionInfo &info) {
const auto operation = g_assertion_failure_handler(info);
ExecuteAssertionFailureOperation(operation, info);
}
}
NOINLINE void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line, const char *format, ...) {
/* Prepare to abort. */
PrepareAbort();
/* Acquire exclusive assert rights. */
if (g_assert_mutex.IsLockedByCurrentThread()) {
AbortWithValue(0);
}
std::scoped_lock lk(g_assert_mutex);
/* Create the assertion info. */
std::va_list vl;
va_start(vl, format);
const ::ams::diag::LogMessage message = { format, std::addressof(vl) };
const AssertionInfo info = {
type,
std::addressof(message),
expr,
func,
file,
line,
};
InvokeAssertionFailureHandler(info);
va_end(vl);
}
void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line) {
return OnAssertionFailure(type, expr, func, file, line, "");
}
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line) {
const Result res = ResultSuccess();
std::va_list vl{};
VAbortImpl(expr, func, file, line, std::addressof(res), nullptr, "", vl);
}
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const char *fmt, ...) {
const Result res = ResultSuccess();
std::va_list vl;
va_start(vl, fmt);
VAbortImpl(expr, func, file, line, std::addressof(res), nullptr, fmt, vl);
}
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const char *fmt, ...) {
std::va_list vl;
va_start(vl, fmt);
VAbortImpl(expr, func, file, line, result, nullptr, fmt, vl);
}
NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const ::ams::os::UserExceptionInfo *exc_info, const char *fmt, ...) {
std::va_list vl;
va_start(vl, fmt);
VAbortImpl(expr, func, file, line, result, exc_info, fmt, vl);
}
NORETURN NOINLINE void VAbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const ::ams::os::UserExceptionInfo *exc_info, const char *fmt, std::va_list vl) {
/* Prepare to abort. */
PrepareAbort();
/* Acquire exclusive abort rights. */
if (g_abort_mutex.IsLockedByCurrentThread()) {
AbortWithValue(result->GetValue());
}
std::scoped_lock lk(g_abort_mutex);
/* Set the abort impl return address. */
impl::SetAbortImplReturnAddress(reinterpret_cast<uintptr_t>(__builtin_return_address(0)));
/* Create abort info. */
std::va_list cvl;
va_copy(cvl, vl);
const diag::LogMessage message = { fmt, std::addressof(cvl) };
const AbortInfo abort_info = {
AbortReason_Abort,
std::addressof(message),
expr,
func,
file,
line,
};
const SdkAbortInfo sdk_abort_info = {
abort_info,
*result,
exc_info
};
/* Invoke observers. */
::ams::diag::impl::InvokeAbortObserver(abort_info);
::ams::diag::impl::InvokeSdkAbortObserver(sdk_abort_info);
/* Abort. */
AbortWithValue(result->GetValue());
}
}
namespace ams::impl {
NORETURN NOINLINE void UnexpectedDefaultImpl(const char *func, const char *file, int line) {
/* Create abort info. */
std::va_list vl{};
const ::ams::diag::LogMessage message = { "" , std::addressof(vl) };
const ::ams::diag::AbortInfo abort_info = {
::ams::diag::AbortReason_UnexpectedDefault,
std::addressof(message),
"",
func,
file,
line,
};
/* Invoke observers. */
::ams::diag::impl::InvokeAbortObserver(abort_info);
/* Abort. */
::ams::diag::AbortWithValue(0);
}
}

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/>.
*/
/* ams::diag::impl::FatalErrorByResultForNx(Result value) */
.section .text._ZN3ams4diag4impl23FatalErrorByResultForNxENS_6ResultE, "ax", %progbits
.global _ZN3ams4diag4impl23FatalErrorByResultForNxENS_6ResultE
.type _ZN3ams4diag4impl23FatalErrorByResultForNxENS_6ResultE, %function
.balign 0x10
_ZN3ams4diag4impl23FatalErrorByResultForNxENS_6ResultE:
/* Save x27/x28. */
stp x27, x28, [sp, #-0x10]!
stp x0, xzr, [sp, #-0x10]!
/* Inline ams::diag::impl::PrepareAbort() */
mrs x27, tpidrro_el0
strh wzr, [x27, #0x100]
ldrh w27, [x27, #0x102]
cbz w27, 0f
svc #0x36
0: /* Restore the value from stack. */
ldr x0, [sp]
add sp, sp, #0x10
/* Put magic std::abort values into x27/x28. */
mov x28, #0xcafe
movk x28, #0xdead, lsl#16
movk x28, #0xf00d, lsl#32
movk x28, #0xa55a, lsl#48
mov x27, #8
/* Abort */
1:
str x28, [x27]
nop
b 1b

View File

@@ -1,50 +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 "impl/diag_get_all_backtrace.hpp"
namespace ams::diag {
size_t GetBacktrace(uintptr_t *out, size_t out_size) {
/* Validate pre-conditions. */
AMS_ASSERT(out != nullptr);
AMS_ASSERT(out_size > 0);
/* Create the backtrace object. */
::ams::diag::Backtrace bt{};
bt.Step();
/* Get the backtrace. */
return ::ams::diag::impl::GetAllBacktrace(out, out_size, bt);
}
#if defined(ATMOSPHERE_OS_HORIZON)
size_t GetBacktrace(uintptr_t *out, size_t out_size, uintptr_t fp, uintptr_t sp, uintptr_t pc) {
/* Validate pre-conditions. */
AMS_ASSERT(out != nullptr);
AMS_ASSERT(out_size > 0);
/* Create the backtrace object. */
::ams::diag::Backtrace bt{fp, sp, pc};
bt.Step();
/* Get the backtrace. */
return ::ams::diag::impl::GetAllBacktrace(out, out_size, bt);
}
#endif
}

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 "diag_log_impl.hpp"
namespace ams::diag::impl {
void CallAllLogObserver(const LogMetaData &meta, const LogBody &body);
namespace {
struct CallPrintDebugString {
void operator()(const LogMetaData &meta, const char *msg, size_t size, bool head, bool tail) {
const LogBody body = {
.message = msg,
.message_size = size,
.is_head = head,
.is_tail = tail
};
CallAllLogObserver(meta, body);
}
};
}
void LogImpl(const LogMetaData &meta, const char *fmt, ...) {
std::va_list vl;
va_start(vl, fmt);
VLogImpl(meta, fmt, vl);
va_end(vl);
}
void VLogImpl(const LogMetaData &meta, const char *fmt, std::va_list vl) {
#if defined(ATMOSPHERE_OS_HORIZON)
/* Print to stack buffer. */
char msg_buffer[DebugPrintBufferLength];
/* TODO: VFormatString using utf-8 printer. */
const size_t len = util::VSNPrintf(msg_buffer, sizeof(msg_buffer), fmt, vl);
#else
/* Print to allocated buffer. */
std::va_list cvl;
va_copy(cvl, vl);
const auto out_len = util::TVSNPrintf(nullptr, 0, fmt, cvl) + 1;
va_end(cvl);
char *msg_buffer = static_cast<char *>(std::malloc(out_len));
AMS_ABORT_UNLESS(msg_buffer != nullptr);
ON_SCOPE_EXIT { std::free(msg_buffer); };
/* TODO: VFormatString using utf-8 printer. */
const size_t len = util::TVSNPrintf(msg_buffer, out_len, fmt, vl);
#endif
/* Call log observer. */
CallPrintDebugString()(meta, msg_buffer, len, true, true);
}
void PutImpl(const LogMetaData &meta, const char *msg, size_t msg_size) {
CallPrintDebugString()(meta, msg, msg_size, true, true);
}
}

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::diag {
namespace impl {
constexpr inline size_t DebugPrintBufferLength = 0x100;
}
}

View File

@@ -1,166 +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 "diag_log_impl.hpp"
#include "impl/diag_observer_manager.hpp"
#include "impl/diag_print_debug_string.hpp"
namespace ams::diag {
namespace impl {
namespace {
constexpr inline size_t DecorationStringLengthMax = 0x61;
constexpr inline const char *EscapeSequencesForSeverity[] = {
"\x1B[90m", /* Dark Gray (Trace) */
nullptr, /* None (Info) */
"\x1B[33m", /* Yellow (Warn) */
"\x1B[31m", /* Red (Error) */
"\x1B[41m\x1B[37m", /* White-on-red (Fatal) */
};
constexpr inline const char EscapeSequenceReset[] = "\x1B[0m";
constexpr inline size_t PrintBufferLength = DecorationStringLengthMax + impl::DebugPrintBufferLength + 1;
constinit os::SdkMutex g_print_buffer_mutex;
constinit char g_print_buffer[PrintBufferLength];
inline void GetCurrentTime(int *h, int *m, int *s, int *ms) {
/* Get the current time. */
const auto cur_time = os::GetSystemTick().ToTimeSpan();
/* Extract fields. */
const s64 hours = cur_time.GetHours();
const s64 minutes = cur_time.GetMinutes();
const s64 seconds = cur_time.GetSeconds();
const s64 milliseconds = cur_time.GetMilliSeconds();
/* Set out fields. */
*h = static_cast<int>(hours);
*m = static_cast<int>(minutes - hours * 60);
*s = static_cast<int>(seconds - minutes * 60);
*ms = static_cast<int>(milliseconds - seconds * 1000);
}
void TentativeDefaultLogObserver(const LogMetaData &meta, const LogBody &body, void *) {
/* Acquire access to the print buffer */
std::scoped_lock lk(g_print_buffer_mutex);
/* Get the escape sequence. */
const char *escape = nullptr;
if (LogSeverity_Trace <= meta.severity && meta.severity <= LogSeverity_Fatal) {
escape = EscapeSequencesForSeverity[meta.severity];
}
/* Declare message variables. */
const char *msg = nullptr;
size_t msg_size = 0;
/* Handle structured logs. */
const bool structured = meta.module_name != nullptr && std::strlen(meta.module_name) >= 2;
if (escape || structured) {
/* Insert timestamp, if head. */
if (structured && body.is_head) {
/* Get current timestamp. */
int hours, minutes, seconds, milliseconds;
GetCurrentTime(std::addressof(hours), std::addressof(minutes), std::addressof(seconds), std::addressof(milliseconds));
/* Print the timestamp/header. */
msg_size += util::SNPrintf(g_print_buffer + msg_size, PrintBufferLength - msg_size, "%s%d:%02d:%02d.%03d [%-5.63s] ", escape ? escape : "", hours, minutes, seconds, milliseconds, meta.module_name[0] == '$' ? meta.module_name + 1 : meta.module_name + 0);
AMS_AUDIT(msg_size <= DecorationStringLengthMax);
} else if (escape) {
msg_size += util::SNPrintf(g_print_buffer + msg_size, PrintBufferLength - msg_size, "%s", escape);
}
/* Determine maximum remaining size. */
const size_t max_msg_size = PrintBufferLength - msg_size - (escape ? sizeof(EscapeSequenceReset) - 1 : 0);
/* Determine printable size. */
size_t printable_size = std::min<size_t>(body.message_size, max_msg_size);
/* Determine newline status. */
bool new_line = false;
if (body.message_size > 0 && body.message[body.message_size - 1] == '\n') {
--printable_size;
new_line = true;
}
/* Print the messsage. */
msg_size += util::SNPrintf(g_print_buffer + msg_size, PrintBufferLength - msg_size, "%.*s%s%s", static_cast<int>(printable_size), body.message, escape ? EscapeSequenceReset : "", new_line ? "\n" : "");
/* Set the message. */
msg = g_print_buffer;
} else {
/* Use the body's message directly. */
msg = body.message;
msg_size = body.message_size;
}
/* Print the string. */
impl::PrintDebugString(msg, msg_size);
}
struct LogObserverContext {
const LogMetaData &meta;
const LogBody &body;
};
using LogObserverManager = ObserverManagerWithDefaultHolder<LogObserverHolder, LogObserverContext>;
constinit LogObserverManager g_log_observer_manager(::ams::diag::InitializeLogObserverHolder, TentativeDefaultLogObserver, nullptr);
}
void CallAllLogObserver(const LogMetaData &meta, const LogBody &body) {
/* Create context. */
const LogObserverContext context = { .meta = meta, .body = body };
/* Invoke the log observer. */
g_log_observer_manager.InvokeAllObserver(context, [] (const LogObserverHolder &holder, const LogObserverContext &context) {
holder.log_observer(context.meta, context.body, holder.arg);
});
}
void ReplaceDefaultLogObserver(LogObserver observer) {
/* Get the default observer. */
auto *default_holder = std::addressof(g_log_observer_manager.GetDefaultObserverHolder());
/* Unregister, replace, and re-register. */
UnregisterLogObserver(default_holder);
InitializeLogObserverHolder(default_holder, observer, nullptr);
RegisterLogObserver(default_holder);
}
void ResetDefaultLogObserver() {
/* Restore the default observer. */
ReplaceDefaultLogObserver(TentativeDefaultLogObserver);
}
}
void RegisterLogObserver(LogObserverHolder *holder) {
impl::g_log_observer_manager.RegisterObserver(holder);
}
void UnregisterLogObserver(LogObserverHolder *holder) {
impl::g_log_observer_manager.UnregisterObserver(holder);
}
}

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 "impl/diag_symbol_impl.hpp"
namespace ams::diag {
uintptr_t GetSymbolName(char *dst, size_t dst_size, uintptr_t address) {
AMS_ASSERT(dst != nullptr);
AMS_ASSERT(dst_size > 0);
AMS_ASSERT(address > 0);
return ::ams::diag::impl::GetSymbolNameImpl(dst, dst_size, address);
}
size_t GetSymbolSize(uintptr_t address) {
AMS_ASSERT(address > 0);
return ::ams::diag::impl::GetSymbolSizeImpl(address);
}
}

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 "diag_abort_observer_manager.hpp"
namespace ams::diag::impl {
AbortObserverManager *GetAbortObserverManager() {
AMS_FUNCTION_LOCAL_STATIC(AbortObserverManager, s_manager);
return std::addressof(s_manager);
}
SdkAbortObserverManager *GetSdkAbortObserverManager() {
AMS_FUNCTION_LOCAL_STATIC(SdkAbortObserverManager, s_manager);
return std::addressof(s_manager);
}
}

View File

@@ -1,28 +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 "diag_observer_manager.hpp"
namespace ams::diag::impl {
using AbortObserverManager = ObserverManager<AbortObserverHolder, const AbortInfo &>;
using SdkAbortObserverManager = ObserverManager<SdkAbortObserverHolder, const SdkAbortInfo &>;
AbortObserverManager *GetAbortObserverManager();
SdkAbortObserverManager *GetSdkAbortObserverManager();
}

View File

@@ -1,135 +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>
#if defined(ATMOSPHERE_OS_WINDOWS)
#include <stratosphere/windows.hpp>
#elif defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
#include <fcntl.h>
#include <unistd.h>
#endif
namespace ams::diag::impl {
namespace {
#if defined(ATMOSPHERE_ARCH_X64)
struct StackFrame {
u64 fp; /* rbp */
u64 lr; /* rip */
};
#elif defined(ATMOSPHERE_ARCH_X86)
struct StackFrame {
u32 fp; /* ebp */
u32 lr; /* eip */
}
#elif defined(ATMOSPHERE_ARCH_ARM64)
struct StackFrame {
u64 fp;
u64 lr;
};
#elif defined(ATMOSPHERE_ARCH_ARM)
struct StackFrame {
u32 fp;
u32 lr;
}
#else
#error "Unknown architecture for generic backtrace."
#endif
bool TryRead(os::NativeHandle native_handle, void *dst, size_t size, const void *address) {
#if defined(ATMOSPHERE_OS_WINDOWS)
return ::ReadProcessMemory(native_handle, address, dst, size, nullptr);
#elif defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
s32 ret;
do {
ret = ::write(native_handle, address, size);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
return false;
}
std::memcpy(dst, address, size);
return true;
#else
#error "Unknown OS for Backtrace native handle"
#endif
}
}
NOINLINE void Backtrace::Initialize() {
/* Clear our size. */
m_index = 0;
m_size = 0;
/* Get the base frame pointer. */
const void *cur_fp = __builtin_frame_address(0);
/* Try to read stack frames, until we run out. */
#if defined(ATMOSPHERE_OS_WINDOWS)
const os::NativeHandle native_handle = ::GetCurrentProcess();
#elif defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
os::NativeHandle pipe_handles[2];
s32 nret;
do { nret = ::pipe(pipe_handles); } while (nret < 0 && errno == EINTR);
if (nret < 0) { return; }
do { nret = ::fcntl(pipe_handles[0], F_SETFL, O_NONBLOCK); } while (nret < 0 && errno == EINTR);
if (nret < 0) { return; }
do { nret = ::fcntl(pipe_handles[1], F_SETFL, O_NONBLOCK); } while (nret < 0 && errno == EINTR);
if (nret < 0) { return; }
ON_SCOPE_EXIT {
do { nret = ::close(pipe_handles[0]); } while (nret < 0 && errno == EINTR);
do { nret = ::close(pipe_handles[1]); } while (nret < 0 && errno == EINTR);
};
const os::NativeHandle native_handle = pipe_handles[1];
if (native_handle < 0) { return; }
#else
#error "Unknown OS for Backtrace native handle"
#endif
StackFrame frame;
while (m_size < BacktraceEntryCountMax) {
/* Clear the frame. */
frame = {};
/* Read the next frame. */
if (!TryRead(native_handle, std::addressof(frame), sizeof(frame), cur_fp)) {
break;
}
/* Add the return address. */
m_backtrace_addresses[m_size++] = reinterpret_cast<void *>(frame.lr);
/* Set the next fp. */
cur_fp = reinterpret_cast<const void *>(frame.fp);
}
}
bool Backtrace::Step() {
return (++m_index) < m_size;
}
uintptr_t Backtrace::GetStackPointer() const {
return 0;
}
uintptr_t Backtrace::GetReturnAddress() const {
return reinterpret_cast<uintptr_t>(m_backtrace_addresses[m_index]);
}
}

View File

@@ -1,217 +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::diag::impl {
namespace {
uintptr_t GetAddressValue(uintptr_t address) {
if (address == 0) {
return 0;
}
if (!util::IsAligned(address, alignof(uintptr_t))) {
return 0;
}
return *reinterpret_cast<uintptr_t *>(address);
}
template<typename T, size_t N>
svc::MemoryInfo *GetMemoryInfoPointer(T (&arr)[N]) {
/* Check that there's enough space. */
static_assert(sizeof(T) * N <= sizeof(svc::MemoryInfo));
static_assert(alignof(T) >= alignof(svc::MemoryInfo));
return reinterpret_cast<svc::MemoryInfo *>(std::addressof(arr[0]));
}
bool IsValidLinkRegisterValue(uintptr_t lr, svc::MemoryInfo *info) {
/* Ensure the memory info is valid. */
Result query_res;
if (!(info->base_address <= lr && lr < info->base_address + info->size) && ({ svc::PageInfo page_info; query_res = svc::QueryMemory(info, std::addressof(page_info), lr); R_FAILED(query_res); })) {
AMS_SDK_LOG("Failed to get backtrace. Query memory failed. (lr: %p, result: %03d-%04d)\n", reinterpret_cast<void *>(lr), query_res.GetModule(), query_res.GetDescription());
return false;
}
/* Check that lr is valid. */
if (lr == 0) {
return false;
}
if (!util::IsAligned(lr, sizeof(u32))) {
AMS_SDK_LOG("Failed to get backtrace. The link register alignment is invalid. (lr: %p)\n", reinterpret_cast<void *>(lr));
return false;
}
/* Check that the lr points to code. */
if (info->permission != svc::MemoryPermission_ReadExecute) {
AMS_SDK_LOG("Failed to get backtrace. The link register points out of the code. (lr: %p)\n", reinterpret_cast<void *>(lr));
return false;
}
return true;
}
void GetNormalStackInfo(Backtrace::StackInfo *out) {
if (void * const fiber = nullptr /* TODO: os::GetCurrentFiber() */; fiber == nullptr) {
/* Get thread. */
auto * const thread = os::GetCurrentThread();
out->stack_top = reinterpret_cast<uintptr_t>(thread->stack);
out->stack_bottom = reinterpret_cast<uintptr_t>(thread->stack) + thread->stack_size;
} else {
/* TODO: Fiber. */
}
}
bool GetExceptionStackInfo(Backtrace::StackInfo *out, uintptr_t sp) {
/* Get the current stack info. */
uintptr_t cur_stack = 0;
size_t cur_stack_size = 0;
os::GetCurrentStackInfo(std::addressof(cur_stack), std::addressof(cur_stack_size));
/* Get the thread's stack info. */
uintptr_t thread_stack = 0;
size_t thread_stack_size = 0;
os::GetThreadStackInfo(std::addressof(thread_stack), std::addressof(thread_stack_size), os::GetCurrentThread());
/* If the current stack is the thread stack, exception stack isn't being used. */
if (cur_stack == thread_stack) {
AMS_ASSERT(cur_stack_size == thread_stack_size);
return false;
}
/* Check if the stack pointer is contained in the current stack. */
if (!(cur_stack <= sp && sp < cur_stack + cur_stack_size)) {
return false;
}
/* Set the output. */
out->stack_top = cur_stack;
out->stack_bottom = cur_stack + cur_stack_size;
return true;
}
}
NOINLINE void Backtrace::Initialize() {
/* Get the stack pointer/frame pointer. */
uintptr_t fp, sp;
__asm__ __volatile__(
#if defined(ATMOSPHERE_ARCH_ARM64)
"mov %[fp], fp\n"
"mov %[sp], sp\n"
#elif defined(ATMOSPHERE_ARCH_ARM)
"mov %[fp], x29\n"
"mov %[sp], sp\n"
#else
#error "Unknown architecture for Horizon fp/sp retrieval."
#endif
: [fp]"=&r"(fp), [sp]"=&r"(sp)
:
: "memory"
);
/* Set our stack info. */
this->SetStackInfo(fp, sp);
/* Step, to get our first lr. */
this->Step();
}
void Backtrace::Initialize(uintptr_t fp, uintptr_t sp, uintptr_t pc) {
/* Set our initial lr. */
m_lr = pc;
/* Set our stack info. */
this->SetStackInfo(fp, sp);
}
bool Backtrace::Step() {
/* We can't step without a frame pointer. */
if (m_fp == 0) {
if (m_current_stack_info != std::addressof(m_normal_stack_info)) {
AMS_SDK_LOG("Failed to get backtrace. The frame pointer is null.\n");
}
return false;
}
/* The frame pointer needs to be aligned. */
if (!util::IsAligned(m_fp, sizeof(uintptr_t))) {
AMS_SDK_LOG("Failed to get backtrace. The frame pointer alignment is invalid. (fp: %p)\n", reinterpret_cast<void *>(m_fp));
return false;
}
/* Ensure our current stack info is good. */
if (!(m_current_stack_info->stack_top <= m_fp && m_fp < m_current_stack_info->stack_bottom)) {
if (m_current_stack_info != std::addressof(m_exception_stack_info) || !(m_normal_stack_info.stack_top <= m_fp && m_fp < m_normal_stack_info.stack_bottom)) {
AMS_SDK_LOG("Failed to get backtrace. The frame pointer points out of the stack. (fp: %p, stack: %p-%p)\n", reinterpret_cast<void *>(m_fp), reinterpret_cast<void *>(m_current_stack_info->stack_top), reinterpret_cast<void *>(m_current_stack_info->stack_bottom));
return false;
}
m_current_stack_info = std::addressof(m_normal_stack_info);
} else if (m_fp <= m_prev_fp) {
AMS_SDK_LOG("Failed to get backtrace. The frame pointer is rewinding. (fp: %p, prev fp: %p, stack: %p-%p)\n", reinterpret_cast<void *>(m_fp), reinterpret_cast<void *>(m_prev_fp), reinterpret_cast<void *>(m_current_stack_info->stack_top), reinterpret_cast<void *>(m_current_stack_info->stack_bottom));
return false;
}
/* Update our previous fp. */
m_prev_fp = m_fp;
/* Read lr/fp. */
m_lr = GetAddressValue(m_fp + sizeof(m_fp));
m_fp = GetAddressValue(m_fp);
/* Check that lr is valid. */
if (IsValidLinkRegisterValue(m_lr, GetMemoryInfoPointer(m_memory_info_buffer))) {
return true;
} else {
m_lr = 0;
return false;
}
}
uintptr_t Backtrace::GetStackPointer() const {
if (m_fp != 0) {
return m_fp - sizeof(m_fp);
} else {
return m_current_stack_info->stack_bottom - sizeof(m_fp);
}
}
uintptr_t Backtrace::GetReturnAddress() const {
return m_lr;
}
void Backtrace::SetStackInfo(uintptr_t fp, uintptr_t sp) {
/* Get the normal stack info. */
GetNormalStackInfo(std::addressof(m_normal_stack_info));
/* Get the exception stack info. */
if (GetExceptionStackInfo(std::addressof(m_exception_stack_info), sp)) {
m_current_stack_info = std::addressof(m_exception_stack_info);
} else {
m_current_stack_info = std::addressof(m_normal_stack_info);
}
/* Set our frame pointer. */
m_fp = fp;
}
}

View File

@@ -1,88 +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 "diag_dump_stack_trace.hpp"
#if defined(ATMOSPHERE_OS_WINDOWS)
#include <stratosphere/windows.hpp>
#endif
namespace ams::diag::impl {
#if defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_DEBUGGING)
namespace {
constexpr const char *ToString(AbortReason reason) {
switch (reason) {
case AbortReason_Audit:
return "Auditing Assertion Failure";
case AbortReason_Assert:
return "Assertion Failure";
case AbortReason_UnexpectedDefault:
return "Unexpected Default";
case AbortReason_Abort:
default:
return "Abort";
}
}
void DefaultPrinter(const AbortInfo &info) {
/* Get the thread name. */
const char *thread_name;
if (auto *cur_thread = os::GetCurrentThread(); cur_thread != nullptr) {
thread_name = os::GetThreadNamePointer(cur_thread);
} else {
thread_name = "unknown";
}
#if defined(ATMOSPHERE_OS_HORIZON)
{
u64 process_id = 0;
u64 thread_id = 0;
svc::GetProcessId(std::addressof(process_id), svc::PseudoHandle::CurrentProcess);
svc::GetThreadId(std::addressof(thread_id), svc::PseudoHandle::CurrentThread);
AMS_SDK_LOG("%s: '%s' in %s, process=0x%02" PRIX64 ", thread=%" PRIu64 " (%s)\n%s:%d\n", ToString(info.reason), info.expr, info.func, process_id, thread_id, thread_name, info.file, info.line);
}
#elif defined(ATMOSPHERE_OS_WINDOWS)
{
DWORD process_id = ::GetCurrentProcessId();
DWORD thread_id = ::GetCurrentThreadId();
AMS_SDK_LOG("%s: '%s' in %s, process=0x%" PRIX64 ", thread=%" PRIu64 " (%s)\n%s:%d\n", ToString(info.reason), info.expr, info.func, static_cast<u64>(process_id), static_cast<u64>(thread_id), thread_name, info.file, info.line);
}
#else
{
AMS_SDK_LOG("%s: '%s' in %s, thread=%s\n%s:%d\n", ToString(info.reason), info.expr, info.func, thread_name, info.file, info.line);
}
#endif
AMS_SDK_VLOG(info.message->fmt, *(info.message->vl));
AMS_SDK_LOG("\n");
TentativeDumpStackTrace();
}
}
#endif
void DefaultAbortObserver(const AbortInfo &info) {
#if defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING)
DefaultPrinter(info);
#else
AMS_UNUSED(info);
#endif
}
}

View File

@@ -1,23 +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::diag::impl {
void TentativeDumpStackTrace();
}

View File

@@ -1,42 +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 "diag_dump_stack_trace.hpp"
namespace ams::diag::impl {
void TentativeDumpStackTrace() {
AMS_SDK_LOG("----------------Stack Trace----------------\n");
{
/* Get the backtrace. */
constexpr size_t MaxBackTraceSize = 0x40;
uintptr_t backtrace[MaxBackTraceSize];
const size_t num_items = ::ams::diag::GetBacktrace(backtrace, MaxBackTraceSize);
/* Print each item. */
for (size_t i = 0; i < num_items; ++i) {
char symbol_name[0x200];
if (const uintptr_t symbol_base = ::ams::diag::GetSymbolName(symbol_name, sizeof(symbol_name), backtrace[i] - 1); symbol_base != 0) {
AMS_SDK_LOG("0x%016" PRIX64 " [ %s+0x%" PRIX64 " ]\n", static_cast<u64>(backtrace[i]), symbol_name, static_cast<u64>(backtrace[i] - symbol_base));
} else {
AMS_SDK_LOG("0x%016" PRIX64 " [ unknown ]\n", static_cast<u64>(backtrace[i]));
}
}
}
AMS_SDK_LOG("-------------------------------------------\n");
}
}

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/>.
*/
#include <stratosphere.hpp>
#include "diag_dump_stack_trace.hpp"
namespace ams::diag::impl {
void TentativeDumpStackTrace() {
/* TODO */
}
}

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/>.
*/
#include <stratosphere.hpp>
namespace ams::diag::impl {
namespace {
constinit uintptr_t g_abort_impl_return_address = std::numeric_limits<uintptr_t>::max();
}
void SetAbortImplReturnAddress(uintptr_t address) {
g_abort_impl_return_address = address;
}
size_t GetAllBacktrace(uintptr_t *out, size_t out_size, ::ams::diag::Backtrace &bt) {
size_t count = 0;
do {
/* Check that we can write another return address. */
if (count >= out_size) {
break;
}
/* Get the current return address. */
const uintptr_t ret_addr = bt.GetReturnAddress();
/* If it's abort impl, reset the trace we're writing. */
if (ret_addr == g_abort_impl_return_address) {
count = 0;
}
/* Set the output pointer. */
out[count++] = ret_addr;
} while (bt.Step());
/* Return the number of addresses written. */
return count;
}
}

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::diag::impl {
void SetAbortImplReturnAddress(uintptr_t address);
size_t GetAllBacktrace(uintptr_t *out, size_t out_size, ::ams::diag::Backtrace &bt);
}

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::diag::impl {
void InvokeAbortObserver(const AbortInfo &info);
void InvokeSdkAbortObserver(const SdkAbortInfo &info);
}

View File

@@ -1,38 +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 "diag_abort_observer_manager.hpp"
#include "diag_invoke_abort.hpp"
namespace ams::diag::impl {
extern bool g_enable_default_abort_observer;
void DefaultAbortObserver(const AbortInfo &info);
void InvokeAbortObserver(const AbortInfo &info) {
if (g_enable_default_abort_observer) {
DefaultAbortObserver(info);
}
GetAbortObserverManager()->InvokeAllObserver(info);
}
void InvokeSdkAbortObserver(const SdkAbortInfo &info) {
GetSdkAbortObserverManager()->InvokeAllObserver(info);
}
}

View File

@@ -1,38 +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 "diag_abort_observer_manager.hpp"
#include "diag_invoke_abort.hpp"
namespace ams::diag::impl {
extern bool g_enable_default_abort_observer;
void DefaultAbortObserver(const AbortInfo &info);
void InvokeAbortObserver(const AbortInfo &info) {
if (g_enable_default_abort_observer) {
DefaultAbortObserver(info);
}
GetAbortObserverManager()->InvokeAllObserver(info);
}
void InvokeSdkAbortObserver(const SdkAbortInfo &info) {
GetSdkAbortObserverManager()->InvokeAllObserver(info);
}
}

View File

@@ -1,61 +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::diag::impl {
namespace {
constexpr inline uintptr_t ModulePathLengthOffset = 4;
constexpr inline uintptr_t ModulePathOffset = 8;
}
uintptr_t GetModuleInfoForHorizon(const char **out_path, size_t *out_path_length, size_t *out_module_size, uintptr_t address) {
/* Check for null address. */
if (address == 0) {
return 0;
}
/* Get module info. */
ro::impl::ExceptionInfo exception_info;
if (!ro::impl::GetExceptionInfo(std::addressof(exception_info), address)) {
return 0;
}
/* Locate the path in the first non-read-execute segment. */
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
auto cur_address = exception_info.module_address;
while (cur_address < exception_info.module_address + exception_info.module_size) {
if (R_FAILED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), cur_address))) {
return 0;
}
if (mem_info.permission != svc::MemoryPermission_ReadExecute) {
break;
}
cur_address += mem_info.size;
}
/* Set output info. */
*out_path = reinterpret_cast<const char *>(cur_address + ModulePathOffset);
*out_path_length = *reinterpret_cast<const u32 *>(cur_address + ModulePathLengthOffset);
*out_module_size = exception_info.module_size;
return exception_info.module_address;
}
}

View File

@@ -1,157 +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::diag::impl {
template<typename Holder, typename Context>
class ObserverManager {
NON_COPYABLE(ObserverManager);
NON_MOVEABLE(ObserverManager);
private:
Holder *m_observer_list_head;
Holder **m_observer_list_tail;
os::ReaderWriterLock m_lock;
public:
constexpr ObserverManager() : m_observer_list_head(nullptr), m_observer_list_tail(std::addressof(m_observer_list_head)), m_lock() {
/* ... */
}
constexpr ~ObserverManager() {
if (std::is_constant_evaluated()) {
this->UnregisterAllObserverLocked();
} else {
this->UnregisterAllObserver();
}
}
void RegisterObserver(Holder *holder) {
/* Acquire a write hold on our lock. */
std::scoped_lock lk(m_lock);
this->RegisterObserverLocked(holder);
}
void UnregisterObserver(Holder *holder) {
/* Acquire a write hold on our lock. */
std::scoped_lock lk(m_lock);
/* Check that we can unregister. */
AMS_ASSERT(holder->is_registered);
/* Remove the holder. */
if (m_observer_list_head == holder) {
m_observer_list_head = holder->next;
if (m_observer_list_tail == std::addressof(holder->next)) {
m_observer_list_tail = std::addressof(m_observer_list_head);
}
} else {
for (auto *cur = m_observer_list_head; cur != nullptr; cur = cur->next) {
if (cur->next == holder) {
cur->next = holder->next;
if (m_observer_list_tail == std::addressof(holder->next)) {
m_observer_list_tail = std::addressof(cur->next);
}
break;
}
}
}
/* Set unregistered. */
holder->next = nullptr;
}
void UnregisterAllObserver() {
/* Acquire a write hold on our lock. */
std::scoped_lock lk(m_lock);
this->UnregisterAllObserverLocked();
}
void InvokeAllObserver(const Context &context) {
/* Use the holder's observer. */
InvokeAllObserver(context, [] (const Holder &holder, const Context &context) {
holder.observer(context);
});
}
template<typename Observer>
void InvokeAllObserver(const Context &context, Observer observer) {
/* Acquire a read hold on our lock. */
std::shared_lock lk(m_lock);
/* Invoke all observers. */
for (const auto *holder = m_observer_list_head; holder != nullptr; holder = holder->next) {
observer(*holder, context);
}
}
protected:
constexpr void RegisterObserverLocked(Holder *holder) {
/* Check that we can register. */
AMS_ASSERT(!holder->is_registered);
/* Insert the holder. */
*m_observer_list_tail = holder;
m_observer_list_tail = std::addressof(holder->next);
/* Set registered. */
holder->next = nullptr;
holder->is_registered = true;
}
constexpr void UnregisterAllObserverLocked() {
/* Unregister all observers. */
for (auto *holder = m_observer_list_head; holder != nullptr; holder = holder->next) {
holder->is_registered = false;
}
/* Reset head/fail. */
m_observer_list_head = nullptr;
m_observer_list_tail = std::addressof(m_observer_list_head);
}
};
template<typename Holder, typename Context>
class ObserverManagerWithDefaultHolder : public ObserverManager<Holder, Context> {
private:
Holder m_default_holder;
public:
template<typename Initializer, typename... Args>
constexpr ObserverManagerWithDefaultHolder(Initializer initializer, Args &&... args) : ObserverManager<Holder, Context>(), m_default_holder{} {
/* Initialize the default observer. */
initializer(std::addressof(m_default_holder), std::forward<Args>(args)...);
/* Register the default observer. */
if (std::is_constant_evaluated()) {
this->RegisterObserverLocked(std::addressof(m_default_holder));
} else {
this->RegisterObserver(std::addressof(m_default_holder));
}
}
Holder &GetDefaultObserverHolder() {
return m_default_holder;
}
const Holder &GetDefaulObservertHolder() const {
return m_default_holder;
}
};
}

View File

@@ -1,29 +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::diag::impl {
void PrintDebugString(const char *msg, size_t size);
inline void PrintDebugString(const char *msg) {
AMS_AUDIT(msg != nullptr);
PrintDebugString(msg, std::strlen(msg));
}
}

View File

@@ -1,29 +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 "diag_print_debug_string.hpp"
namespace ams::diag::impl {
void PrintDebugString(const char *msg, size_t size) {
AMS_AUDIT(msg != nullptr || size == 0);
if (size != 0) {
svc::OutputDebugString(msg, size);
}
}
}

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/>.
*/
#include <stratosphere.hpp>
#include "diag_print_debug_string.hpp"
namespace ams::diag::impl {
NOINLINE void PrintDebugString(const char *msg, size_t size) {
AMS_AUDIT(msg != nullptr || size == 0);
if (size == 0) {
return;
}
/* TODO: Printf? */
printf("%s", msg);
}
}

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/>.
*/
#include <stratosphere.hpp>
#include "diag_print_debug_string.hpp"
namespace ams::diag::impl {
NOINLINE void PrintDebugString(const char *msg, size_t size) {
AMS_AUDIT(msg != nullptr || size == 0);
if (size == 0) {
return;
}
/* TODO: Printf? */
printf("%s", msg);
}
}

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/>.
*/
#include <stratosphere.hpp>
#include "diag_print_debug_string.hpp"
namespace ams::diag::impl {
NOINLINE void PrintDebugString(const char *msg, size_t size) {
AMS_AUDIT(msg != nullptr || size == 0);
if (size == 0) {
return;
}
/* TODO: OutputDebugString? Printf? */
printf("%s", msg);
}
}

View File

@@ -1,102 +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>
/* TODO: Rename, if we change to e.g. use amsMain? */
extern "C" int main(int argc, char **argv);
namespace ams::diag::impl {
uintptr_t GetModuleInfoForHorizon(const char **out_path, size_t *out_path_length, size_t *out_module_size, uintptr_t address);
namespace {
const char *GetLastCharacterPointer(const char *str, size_t len, char c) {
for (const char *last = str + len - 1; last >= str; --last) {
if (*last == c) {
return last;
}
}
return nullptr;
}
void GetFileNameWithoutExtension(const char **out, size_t *out_size, const char *path, size_t path_length) {
const auto last_sep1 = GetLastCharacterPointer(path, path_length, '\\');
const auto last_sep2 = GetLastCharacterPointer(path, path_length, '/');
const auto ext = GetLastCharacterPointer(path, path_length, '.');
/* Handle last-separator. */
if (last_sep1 && last_sep2) {
if (last_sep1 > last_sep2) {
*out = last_sep1 + 1;
} else {
*out = last_sep2 + 1;
}
} else if (last_sep1) {
*out = last_sep1 + 1;
} else if (last_sep2) {
*out = last_sep2 + 1;
} else {
*out = path;
}
/* Handle extension. */
if (ext && ext >= *out) {
*out_size = ext - *out;
} else {
*out_size = (path + path_length) - *out;
}
}
constinit const char *g_process_name = nullptr;
constinit size_t g_process_name_size = 0;
constinit os::SdkMutex g_process_name_lock;
constinit bool g_got_process_name = false;
void EnsureProcessNameCached() {
/* Ensure process name. */
if (AMS_UNLIKELY(!g_got_process_name)) {
std::scoped_lock lk(g_process_name_lock);
if (AMS_LIKELY(!g_got_process_name)) {
const char *path;
size_t path_length;
size_t module_size;
if (GetModuleInfoForHorizon(std::addressof(path), std::addressof(path_length), std::addressof(module_size), reinterpret_cast<uintptr_t>(main)) != 0) {
GetFileNameWithoutExtension(std::addressof(g_process_name), std::addressof(g_process_name_size), path, path_length);
AMS_ASSERT(g_process_name_size == 0 || util::VerifyUtf8String(g_process_name, g_process_name_size));
} else {
g_process_name = "";
g_process_name_size = 0;
}
g_got_process_name = true;
}
}
}
}
void GetProcessNamePointer(const char **out, size_t *out_size) {
/* Ensure process name is cached. */
EnsureProcessNameCached();
/* Get cached process name. */
*out = g_process_name;
*out_size = g_process_name_size;
}
}

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::diag::impl {
uintptr_t GetSymbolNameImpl(char *dst, size_t dst_size, uintptr_t address);
size_t GetSymbolSizeImpl(uintptr_t address);
}

View File

@@ -1,241 +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 "diag_symbol_impl.hpp"
#define PACKAGE "stratosphere"
#define PACKAGE_VERSION STRINGIFY(ATMOSPHERE_RELEASE_VERSION_MAJOR.ATMOSPHERE_RELEASE_VERSION_MINOR.ATMOSPHERE_RELEASE_VERSION_MICRO)
#if defined(ATMOSPHERE_OS_LINUX)
#include <bfd.h>
#include <unistd.h>
#include <dlfcn.h>
#include <link.h>
extern "C" char __init_array_start;
#endif
#include <cxxabi.h>
namespace ams::diag::impl {
namespace {
class BfdHelper {
private:
bfd *m_handle;
asymbol **m_symbol;
size_t m_num_symbol;
size_t m_num_func_symbol;
const char *m_module_name;
uintptr_t m_module_address;
size_t m_module_size;
private:
BfdHelper() : m_handle(nullptr), m_symbol(nullptr), m_module_name(nullptr) {
/* Get the current executable name. */
char exe_path[4_KB] = {};
GetExecutablePath(exe_path, sizeof(exe_path));
/* Open bfd. */
bfd *b = ::bfd_openr(exe_path, 0);
if (b == nullptr) {
return;
}
auto bfd_guard = SCOPE_GUARD { ::bfd_close(b); };
/* Check the format. */
if (!::bfd_check_format(b, bfd_object)) {
return;
}
/* Verify the file has symbols. */
if ((bfd_get_file_flags(b) & HAS_SYMS) == 0) {
return;
}
/* Read the symbols. */
unsigned int _;
void *symbol_table;
s64 num_symbols = bfd_read_minisymbols(b, false, std::addressof(symbol_table), std::addressof(_));
if (num_symbols == 0) {
num_symbols = bfd_read_minisymbols(b, true, std::addressof(symbol_table), std::addressof(_));
if (num_symbols < 0) {
return;
}
}
/* We successfully got the symbol table. */
bfd_guard.Cancel();
m_handle = b;
m_symbol = reinterpret_cast<asymbol **>(symbol_table);
m_num_symbol = static_cast<size_t>(num_symbols);
/* Sort the symbol table. */
std::sort(m_symbol + 0, m_symbol + m_num_symbol, [] (asymbol *lhs, asymbol *rhs) {
const bool l_func = (lhs->flags & BSF_FUNCTION);
const bool r_func = (rhs->flags & BSF_FUNCTION);
if (l_func == r_func) {
return bfd_asymbol_value(lhs) < bfd_asymbol_value(rhs);
} else {
return l_func;
}
});
/* Determine number of function symbols. */
m_num_func_symbol = 0;
for (size_t i = 0; i < m_num_symbol; ++i) {
if ((m_symbol[i]->flags & BSF_FUNCTION) == 0) {
m_num_func_symbol = i;
break;
}
}
for (int i = std::strlen(exe_path) - 1; i >= 0; --i) {
if (exe_path[i] == '/' || exe_path[i] == '\\') {
m_module_name = strdup(exe_path + i + 1);
break;
}
}
/* Get our module base/size. */
#if defined(ATMOSPHERE_OS_LINUX)
{
m_module_address = _r_debug.r_map->l_addr;
m_module_size = reinterpret_cast<uintptr_t>(std::addressof(__init_array_start)) - m_module_address;
}
#endif
}
~BfdHelper() {
if (m_symbol != nullptr) {
std::free(m_symbol);
}
if (m_handle != nullptr) {
::bfd_close(m_handle);
}
}
public:
static BfdHelper &GetInstance() {
AMS_FUNCTION_LOCAL_STATIC(BfdHelper, s_bfd_helper_instance);
return s_bfd_helper_instance;
}
private:
size_t GetSymbolSizeImpl(asymbol **symbol) const {
/* Do our best to guess. */
const auto vma = bfd_asymbol_value(*symbol);
if (symbol != m_symbol + m_num_func_symbol - 1) {
return bfd_asymbol_value(*(symbol + 1)) - vma;
} else {
const auto *sec = (*symbol)->section;
return (sec->vma + sec->size) - vma;
}
}
std::ptrdiff_t GetSymbolAddressDisplacement(uintptr_t address) const {
std::ptrdiff_t displacement = 0;
if (m_module_address <= address && address < m_module_address + m_module_size) {
displacement = m_module_address;
}
return displacement;
}
asymbol **GetBestSymbol(uintptr_t address) const {
/* Adjust the symbol address. */
address -= this->GetSymbolAddressDisplacement(address);
asymbol **best_symbol = std::lower_bound(m_symbol + 0, m_symbol + m_num_func_symbol, address, [](asymbol *lhs, uintptr_t rhs) {
return bfd_asymbol_value(lhs) < rhs;
});
if (best_symbol == m_symbol + m_num_func_symbol) {
return nullptr;
}
if (bfd_asymbol_value(*best_symbol) != address && best_symbol > m_symbol) {
--best_symbol;
}
const auto vma = bfd_asymbol_value(*best_symbol);
const auto end = vma + this->GetSymbolSizeImpl(best_symbol);
if (vma <= address && address < end) {
return best_symbol;
} else {
return nullptr;
}
}
public:
uintptr_t GetSymbolName(char *dst, size_t dst_size, uintptr_t address) const {
/* Get the symbol. */
auto **symbol = this->GetBestSymbol(address);
if (symbol == nullptr) {
return 0;
}
/* Print the symbol. */
const char *name = bfd_asymbol_name(*symbol);
int cpp_name_status = 0;
if (char *demangled = abi::__cxa_demangle(name, nullptr, 0, std::addressof(cpp_name_status)); cpp_name_status == 0) {
AMS_ASSERT(demangled != nullptr);
util::TSNPrintf(dst, dst_size, "%s", demangled);
std::free(demangled);
} else {
util::TSNPrintf(dst, dst_size, "%s", name);
}
return bfd_asymbol_value(*symbol) + this->GetSymbolAddressDisplacement(address);
}
size_t GetSymbolSize(uintptr_t address) const {
/* Get the symbol. */
auto **symbol = this->GetBestSymbol(address);
if (symbol == nullptr) {
return 0;
}
return this->GetSymbolSizeImpl(symbol);
}
private:
static void GetExecutablePath(char *dst, size_t dst_size) {
#if defined(ATMOSPHERE_OS_LINUX)
{
if (::readlink("/proc/self/exe", dst, dst_size) == -1) {
dst[0] = 0;
return;
}
}
#else
#error "Unknown OS for BfdHelper GetExecutablePath"
#endif
}
};
}
uintptr_t GetSymbolNameImpl(char *dst, size_t dst_size, uintptr_t address) {
return BfdHelper::GetInstance().GetSymbolName(dst, dst_size, address);
}
size_t GetSymbolSizeImpl(uintptr_t address) {
return BfdHelper::GetInstance().GetSymbolSize(address);
}
}

View File

@@ -1,31 +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 "diag_symbol_impl.hpp"
namespace ams::diag::impl {
uintptr_t GetSymbolNameImpl(char *dst, size_t dst_size, uintptr_t address) {
AMS_UNUSED(dst, dst_size, address);
AMS_ABORT("TODO");
}
size_t GetSymbolSizeImpl(uintptr_t address) {
AMS_UNUSED(address);
AMS_ABORT("TODO");
}
}

View File

@@ -1,318 +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 "diag_symbol_impl.hpp"
#include <unistd.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <mach-o/stab.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/param.h>
#include <sys/mman.h>
#include <cxxabi.h>
extern "C" {
void __module_offset_helper() { /* ... */ }
}
namespace ams::diag::impl {
namespace {
class CurrentExecutableHelper {
private:
struct SymbolInfo {
uintptr_t address;
const char *name;
};
private:
os::NativeHandle m_fd;
void *m_file_map;
size_t m_file_size;
SymbolInfo *m_symbols;
size_t m_num_symbol;
const char *m_module_name;
uintptr_t m_module_address;
size_t m_module_size;
uintptr_t m_module_displacement;
private:
CurrentExecutableHelper() : m_fd(-1), m_file_map(nullptr), m_file_size(0), m_symbols(nullptr), m_num_symbol(0), m_module_name(nullptr), m_module_address(0), m_module_size(0), m_module_displacement(0) {
/* Get the current executable name. */
char exe_path[4_KB] = {};
GetExecutablePath(exe_path, sizeof(exe_path));
/* Open the current executable. */
os::NativeHandle fd;
do {
fd = ::open(exe_path, O_RDONLY);
} while (fd < 0 && errno == EINTR);
if (fd < 0) {
return;
}
ON_SCOPE_EXIT { if (fd >= 0) { s32 ret; do { ret = ::close(fd); } while (ret < 0 && errno == EINTR); } };
/* Get the file size. */
struct stat st;
if (fstat(fd, std::addressof(st)) < 0) {
return;
}
/* Check that the file can be mapped. */
const size_t exe_size = st.st_size;
if (exe_size == 0) {
return;
}
/* Map the executable. */
void *exe_map = mmap(nullptr, exe_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (exe_map == MAP_FAILED) {
return;
}
ON_SCOPE_EXIT { if (exe_map != nullptr) { munmap(exe_map, exe_size); } };
/* Get the file's u32 magic. */
const uintptr_t exe_start = reinterpret_cast<uintptr_t>(exe_map);
const u32 magic = *reinterpret_cast<const u32 *>(exe_start);
/* Get/parse the mach header. */
u32 ncmds;
bool is_64;
if (magic == MH_MAGIC) {
const auto *header = reinterpret_cast<const struct mach_header *>(exe_start);
ncmds = header->ncmds;
is_64 = false;
} else if (magic == MH_MAGIC_64) {
const auto *header = reinterpret_cast<const struct mach_header_64 *>(exe_start);
ncmds = header->ncmds;
is_64 = true;
} else {
return;
}
/* Find the symbol load command. */
const auto *lc = reinterpret_cast<const struct load_command *>(exe_start + (is_64 ? sizeof(struct mach_header_64) : sizeof(struct mach_header)));
for (u32 i = 0; i < ncmds; ++i) {
/* If we encounter the symbol table, parse it. */
if (lc->cmd == LC_SYMTAB) {
if (is_64) {
this->ParseSymbolTable<struct nlist_64>(exe_start, reinterpret_cast<const struct symtab_command *>(lc));
} else {
this->ParseSymbolTable<struct nlist>(exe_start, reinterpret_cast<const struct symtab_command *>(lc));
}
break;
} else if (lc->cmd == LC_SEGMENT) {
const auto *sc = reinterpret_cast<const struct segment_command *>(lc);
if (std::strcmp(sc->segname, "__TEXT") == 0) {
AMS_ASSERT(m_module_address == 0);
m_module_address = sc->vmaddr;
m_module_size = sc->vmsize;
AMS_ASSERT(m_module_address != 0);
}
} else if (lc->cmd == LC_SEGMENT_64) {
const auto *sc = reinterpret_cast<const struct segment_command_64 *>(lc);
if (std::strcmp(sc->segname, "__TEXT") == 0) {
AMS_ASSERT(m_module_address == 0);
m_module_address = sc->vmaddr;
m_module_size = sc->vmsize;
AMS_ASSERT(m_module_address != 0);
}
}
/* Advance to the next load command. */
lc = reinterpret_cast<const struct load_command *>(reinterpret_cast<uintptr_t>(lc) + lc->cmdsize);
}
for (size_t i = 0; i < m_num_symbol; ++i) {
if (std::strcmp(m_symbols[i].name, "___module_offset_helper") == 0) {
m_module_displacement = reinterpret_cast<uintptr_t>(&__module_offset_helper) - m_symbols[i].address;
break;
}
}
if (m_module_address > 0 && m_module_size > 0 && m_num_symbol > 0) {
std::swap(m_fd, fd);
std::swap(m_file_map, exe_map);
m_file_size = exe_size;
}
}
~CurrentExecutableHelper() {
if (m_file_map != nullptr) {
munmap(m_file_map, m_file_size);
}
if (m_fd >= 0) {
s32 ret;
do { ret = ::close(m_fd); } while (ret < 0 && errno == EINTR);
}
}
public:
static CurrentExecutableHelper &GetInstance() {
AMS_FUNCTION_LOCAL_STATIC(CurrentExecutableHelper, s_current_executable_helper_instance);
return s_current_executable_helper_instance;
}
private:
template<typename NlistType>
void ParseSymbolTable(uintptr_t exe_start, const struct symtab_command *c) {
/* Check pre-conditions. */
AMS_ASSERT(m_fd == -1);
AMS_ASSERT(m_file_map == nullptr);
AMS_ASSERT(m_symbols == nullptr);
/* Get the strtab/symtab. */
const auto *symtab = reinterpret_cast<const NlistType *>(exe_start + c->symoff);
const char *strtab = reinterpret_cast<const char *>(exe_start + c->stroff);
/* Determine the number of functions. */
size_t funcs = 0;
for (size_t i = 0; i < c->nsyms; ++i) {
if (symtab[i].n_type != N_FUN || symtab[i].n_sect == NO_SECT) {
continue;
}
++funcs;
}
/* Allocate functions. */
m_symbols = reinterpret_cast<SymbolInfo *>(std::malloc(sizeof(SymbolInfo) * funcs));
if (m_symbols == nullptr) {
return;
}
/* Set all symbols. */
m_num_symbol = 0;
for (size_t i = 0; i < c->nsyms; ++i) {
if (symtab[i].n_type != N_FUN || symtab[i].n_sect == NO_SECT) {
continue;
}
m_symbols[m_num_symbol].address = symtab[i].n_value;
m_symbols[m_num_symbol].name = strtab + symtab[i].n_un.n_strx;
++m_num_symbol;
}
AMS_ASSERT(m_num_symbol == funcs);
/* Sort the symbols. */
std::sort(m_symbols + 0, m_symbols + m_num_symbol, [] (const SymbolInfo &lhs, const SymbolInfo &rhs) {
return lhs.address < rhs.address;
});
}
size_t GetSymbolSizeImpl(const SymbolInfo *symbol) const {
/* Do our best to guess. */
if (symbol != m_symbols + m_num_symbol - 1) {
return (symbol + 1)->address - symbol->address;
} else if (m_module_address + m_module_size >= symbol->address) {
return m_module_address + m_module_size - symbol->address;
} else {
return 0;
}
}
const SymbolInfo *GetBestSymbol(uintptr_t address) const {
address -= m_module_displacement;
const SymbolInfo *best_symbol = std::lower_bound(m_symbols + 0, m_symbols + m_num_symbol, address, [](const SymbolInfo &lhs, uintptr_t rhs) {
return lhs.address < rhs;
});
if (best_symbol == m_symbols + m_num_symbol) {
return nullptr;
}
if (best_symbol->address != address && best_symbol > m_symbols) {
--best_symbol;
}
const auto vma = best_symbol->address;
const auto end = vma + this->GetSymbolSizeImpl(best_symbol);
if (vma <= address && address < end) {
return best_symbol;
} else {
return nullptr;
}
}
public:
uintptr_t GetSymbolName(char *dst, size_t dst_size, uintptr_t address) const {
if (m_fd < 0) {
return 0;
}
/* Get the symbol. */
const auto *symbol = this->GetBestSymbol(address);
if (symbol == nullptr) {
return 0;
}
/* Print the symbol. */
const char *name = symbol->name;
int cpp_name_status = 0;
if (char *demangled = abi::__cxa_demangle(name, nullptr, 0, std::addressof(cpp_name_status)); cpp_name_status == 0) {
AMS_ASSERT(demangled != nullptr);
util::TSNPrintf(dst, dst_size, "%s", demangled);
std::free(demangled);
} else {
util::TSNPrintf(dst, dst_size, "%s", name);
}
return symbol->address + m_module_displacement;
}
size_t GetSymbolSize(uintptr_t address) const {
if (m_fd < 0) {
return 0;
}
/* Get the symbol. */
const auto *symbol = this->GetBestSymbol(address);
if (symbol == nullptr) {
return 0;
}
return this->GetSymbolSizeImpl(symbol);
}
private:
static void GetExecutablePath(char *dst, size_t dst_size) {
u32 len = dst_size;
if (_NSGetExecutablePath(dst, std::addressof(len)) != 0) {
dst[0] = 0;
return;
}
}
};
}
uintptr_t GetSymbolNameImpl(char *dst, size_t dst_size, uintptr_t address) {
return CurrentExecutableHelper::GetInstance().GetSymbolName(dst, dst_size, address);
}
size_t GetSymbolSizeImpl(uintptr_t address) {
return CurrentExecutableHelper::GetInstance().GetSymbolSize(address);
}
}

View File

@@ -1,292 +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 <stratosphere/windows.hpp>
#include "diag_symbol_impl.hpp"
#include <cxxabi.h>
extern "C" {
void __module_offset_helper() { /* ... */ }
}
namespace ams::diag::impl {
namespace {
class CurrentExecutableHelper {
private:
struct SymbolInfo {
uintptr_t address;
const char *name;
};
private:
os::NativeHandle m_file_handle;
os::NativeHandle m_map_handle;
void *m_file_map;
size_t m_file_size;
SymbolInfo *m_symbols;
size_t m_num_symbol;
uintptr_t m_module_address;
size_t m_module_size;
uintptr_t m_module_displacement;
private:
CurrentExecutableHelper() : m_file_handle(INVALID_HANDLE_VALUE), m_map_handle(INVALID_HANDLE_VALUE), m_file_map(nullptr), m_file_size(0), m_symbols(nullptr), m_num_symbol(0), m_module_address(0), m_module_size(0), m_module_displacement(0) {
/* Open the current executable. */
auto exe_handle = OpenExecutableFile();
if (exe_handle == INVALID_HANDLE_VALUE) {
return;
}
ON_SCOPE_EXIT { if (exe_handle != INVALID_HANDLE_VALUE) { ::CloseHandle(exe_handle); } };
/* Get the exe size. */
LARGE_INTEGER exe_size_li;
if (::GetFileSizeEx(exe_handle, std::addressof(exe_size_li)) == 0) {
return;
}
/* Check the exe size. */
s64 exe_size = exe_size_li.QuadPart;
if (exe_size == 0) {
return;
}
/* Create a file mapping. */
auto map_handle = ::CreateFileMappingW(exe_handle, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (map_handle == INVALID_HANDLE_VALUE) {
return;
}
ON_SCOPE_EXIT { if (map_handle != INVALID_HANDLE_VALUE) { ::CloseHandle(map_handle); } };
/* Map the file. */
void *exe_map = ::MapViewOfFile(map_handle, FILE_MAP_READ, 0, 0, 0);
if (exe_map == nullptr) {
return;
}
ON_SCOPE_EXIT { if (exe_map != nullptr) { ::UnmapViewOfFile(exe_map); } };
/* Get/parse the DOS header. */
const uintptr_t exe_start = reinterpret_cast<uintptr_t>(exe_map);
const auto *dos_header = reinterpret_cast<const IMAGE_DOS_HEADER *>(exe_start);
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
return;
}
/* Set up the nt headers. */
const auto *nt32 = reinterpret_cast<const IMAGE_NT_HEADERS32 *>(exe_start + dos_header->e_lfanew);
const auto *nt64 = reinterpret_cast<const IMAGE_NT_HEADERS64 *>(exe_start + dos_header->e_lfanew);
if (nt32->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 || nt32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64) {
nt32 = nullptr;
} else {
nt64 = nullptr;
}
#define EXE_NT_HEADER(x) ((nt64 != nullptr) ? nt64->x : nt32->x)
if (EXE_NT_HEADER(Signature) != IMAGE_NT_SIGNATURE) {
return;
}
/* Check that the optional header is really present. */
if (EXE_NT_HEADER(FileHeader.SizeOfOptionalHeader) < sizeof(IMAGE_OPTIONAL_HEADER32)) {
return;
}
/* Get sections. */
const auto *sec = nt64 != nullptr ? IMAGE_FIRST_SECTION(nt64) : IMAGE_FIRST_SECTION(nt32);
/* Get the symbol table. */
const auto *symtab = reinterpret_cast<const IMAGE_SYMBOL *>(exe_start + EXE_NT_HEADER(FileHeader.PointerToSymbolTable));
const size_t nsym = EXE_NT_HEADER(FileHeader.NumberOfSymbols);
const char *strtab = reinterpret_cast<const char *>(symtab + nsym);
/* Find the section containing our code. */
int text_section = -1;
for (size_t i = 0; i < nsym; ++i) {
if (ISFCN(symtab[i].Type)) {
const char *name = symtab[i].N.Name.Short == 0 ? strtab + symtab[i].N.Name.Long : reinterpret_cast<const char *>(symtab[i].N.ShortName);
if (std::strcmp(name, "__module_offset_helper") == 0) {
AMS_ASSERT(text_section == -1);
AMS_ASSERT(m_module_displacement == 0);
m_module_displacement = reinterpret_cast<uintptr_t>(&__module_offset_helper) - symtab[i].Value;
text_section = symtab[i].SectionNumber;
AMS_ASSERT(m_module_displacement != 0);
AMS_ASSERT(text_section != -1);
break;
}
}
}
/* Determine the number of functions. */
size_t funcs = 0;
for (size_t i = 0; i < nsym; ++i) {
if (ISFCN(symtab[i].Type) && symtab[i].SectionNumber == text_section) {
++funcs;
}
}
/* Allocate functions. */
m_symbols = reinterpret_cast<SymbolInfo *>(std::malloc(sizeof(SymbolInfo) * funcs));
if (m_symbols == nullptr) {
return;
}
/* Set all symbols. */
m_num_symbol = 0;
for (size_t i = 0; i < nsym; ++i) {
if (ISFCN(symtab[i].Type) && symtab[i].SectionNumber == text_section) {
m_symbols[m_num_symbol].address = symtab[i].Value;
m_symbols[m_num_symbol].name = symtab[i].N.Name.Short == 0 ? strtab + symtab[i].N.Name.Long : reinterpret_cast<const char *>(symtab[i].N.ShortName);
++m_num_symbol;
}
}
AMS_ASSERT(m_num_symbol == funcs);
/* Sort the symbols. */
std::sort(m_symbols + 0, m_symbols + m_num_symbol, [] (const SymbolInfo &lhs, const SymbolInfo &rhs) {
return lhs.address < rhs.address;
});
m_module_address = 0;
m_module_size = sec[text_section - 1].Misc.VirtualSize;
if (m_module_displacement != 0 && m_module_size > 0 && m_num_symbol > 0) {
std::swap(m_file_handle, exe_handle);
std::swap(m_map_handle, map_handle);
std::swap(m_file_map, exe_map);
m_file_size = exe_size;
}
}
~CurrentExecutableHelper() {
if (m_file_map != nullptr) {
::UnmapViewOfFile(m_file_map);
}
if (m_map_handle != nullptr) {
::CloseHandle(m_map_handle);
}
if (m_file_handle != nullptr) {
::CloseHandle(m_file_handle);
}
}
public:
static CurrentExecutableHelper &GetInstance() {
AMS_FUNCTION_LOCAL_STATIC(CurrentExecutableHelper, s_current_executable_helper_instance);
return s_current_executable_helper_instance;
}
private:
size_t GetSymbolSizeImpl(const SymbolInfo *symbol) const {
/* Do our best to guess. */
if (symbol != m_symbols + m_num_symbol - 1) {
return (symbol + 1)->address - symbol->address;
} else if (m_module_address + m_module_size >= symbol->address) {
return m_module_address + m_module_size - symbol->address;
} else {
return 0;
}
}
const SymbolInfo *GetBestSymbol(uintptr_t address) const {
address -= m_module_displacement;
const SymbolInfo *best_symbol = std::lower_bound(m_symbols + 0, m_symbols + m_num_symbol, address, [](const SymbolInfo &lhs, uintptr_t rhs) {
return lhs.address < rhs;
});
if (best_symbol == m_symbols + m_num_symbol) {
return nullptr;
}
if (best_symbol->address != address && best_symbol > m_symbols) {
--best_symbol;
}
const auto vma = best_symbol->address;
const auto end = vma + this->GetSymbolSizeImpl(best_symbol);
if (vma <= address && address < end) {
return best_symbol;
} else {
return nullptr;
}
}
public:
uintptr_t GetSymbolName(char *dst, size_t dst_size, uintptr_t address) const {
if (m_file_handle == INVALID_HANDLE_VALUE) {
return 0;
}
/* Get the symbol. */
const auto *symbol = this->GetBestSymbol(address);
if (symbol == nullptr) {
return 0;
}
/* Print the symbol. */
const char *name = symbol->name;
int cpp_name_status = 0;
if (char *demangled = abi::__cxa_demangle(name, nullptr, 0, std::addressof(cpp_name_status)); cpp_name_status == 0) {
AMS_ASSERT(demangled != nullptr);
util::TSNPrintf(dst, dst_size, "%s", demangled);
std::free(demangled);
} else {
util::TSNPrintf(dst, dst_size, "%s", name);
}
return symbol->address + m_module_displacement;
}
size_t GetSymbolSize(uintptr_t address) const {
if (m_file_handle == INVALID_HANDLE_VALUE) {
return 0;
}
/* Get the symbol. */
const auto *symbol = this->GetBestSymbol(address);
if (symbol == nullptr) {
return 0;
}
return this->GetSymbolSizeImpl(symbol);
}
private:
static os::NativeHandle OpenExecutableFile() {
/* Get the module file name. */
wchar_t module_file_name[0x1000];
if (::GetModuleFileNameW(0, module_file_name, util::size(module_file_name)) == 0) {
return INVALID_HANDLE_VALUE;
}
return ::CreateFileW(module_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
}
};
}
uintptr_t GetSymbolNameImpl(char *dst, size_t dst_size, uintptr_t address) {
return CurrentExecutableHelper::GetInstance().GetSymbolName(dst, dst_size, address);
}
size_t GetSymbolSizeImpl(uintptr_t address) {
return CurrentExecutableHelper::GetInstance().GetSymbolSize(address);
}
}

View File

@@ -1,91 +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::diag::impl {
namespace {
bool IsHeadOfCharacter(u8 c) {
return (c & 0xC0) != 0x80;
}
size_t GetCharacterSize(u8 c) {
if ((c & 0x80) == 0) {
return 1;
} else if ((c & 0xE0) == 0xC0) {
return 2;
} else if ((c & 0xF0) == 0xE0) {
return 3;
} else if ((c & 0xF8) == 0xF0) {
return 4;
}
return 0;
}
const char *FindLastCharacterPointer(const char *str, size_t len) {
/* Find the head of the last character. */
const char *cur;
for (cur = str + len - 1; cur >= str && !IsHeadOfCharacter(*reinterpret_cast<const u8 *>(cur)); --cur) {
/* ... */
}
/* Return the last character. */
if (AMS_LIKELY(cur >= str)) {
return cur;
} else {
return nullptr;
}
}
}
int GetValidSizeAsUtf8String(const char *str, size_t len) {
/* Check pre-condition. */
AMS_ASSERT(str != nullptr);
/* Check if we have no data. */
if (len == 0) {
return 0;
}
/* Get the last character pointer. */
const auto *last_char_ptr = FindLastCharacterPointer(str, len);
if (last_char_ptr == nullptr) {
return -1;
}
/* Get sizes. */
const size_t actual_size = (str + len) - last_char_ptr;
const size_t last_char_size = GetCharacterSize(*reinterpret_cast<const u8 *>(last_char_ptr));
if (last_char_size == 0) {
return -1;
} else if (actual_size >= last_char_size) {
if (actual_size == last_char_size) {
return len;
} else {
return -1;
}
} else if (actual_size >= len) {
AMS_ASSERT(actual_size == len);
return -1;
} else {
return static_cast<int>(len - actual_size);
}
}
}

View File

@@ -1,98 +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
typedef struct {
u64 base;
u64 size;
} DmntMemoryRegionExtents;
typedef struct {
u64 process_id;
u64 title_id;
DmntMemoryRegionExtents main_nso_extents;
DmntMemoryRegionExtents heap_extents;
DmntMemoryRegionExtents alias_extents;
DmntMemoryRegionExtents address_space_extents;
u8 main_nso_build_id[0x20];
} DmntCheatProcessMetadata;
typedef struct {
char readable_name[0x40];
uint32_t num_opcodes;
uint32_t opcodes[0x100];
} DmntCheatDefinition;
typedef struct {
bool enabled;
uint32_t cheat_id;
DmntCheatDefinition definition;
} DmntCheatEntry;
typedef struct {
u64 value;
u8 width;
} DmntFrozenAddressValue;
typedef struct {
u64 address;
DmntFrozenAddressValue value;
} DmntFrozenAddressEntry;
Result dmntchtInitialize(void);
void dmntchtExit(void);
Service* dmntchtGetServiceSession(void);
Result dmntchtHasCheatProcess(bool *out);
Result dmntchtGetCheatProcessEvent(Event *event);
Result dmntchtGetCheatProcessMetadata(DmntCheatProcessMetadata *out_metadata);
Result dmntchtForceOpenCheatProcess(void);
Result dmntchtForceCloseCheatProcess(void);
Result dmntchtGetCheatProcessMappingCount(u64 *out_count);
Result dmntchtGetCheatProcessMappings(MemoryInfo *buffer, u64 max_count, u64 offset, u64 *out_count);
Result dmntchtReadCheatProcessMemory(u64 address, void *buffer, size_t size);
Result dmntchtWriteCheatProcessMemory(u64 address, const void *buffer, size_t size);
Result dmntchtQueryCheatProcessMemory(MemoryInfo *mem_info, u64 address);
Result dmntchtPauseCheatProcess(void);
Result dmntchtResumeCheatProcess(void);
Result dmntchtGetCheatCount(u64 *out_count);
Result dmntchtGetCheats(DmntCheatEntry *buffer, u64 max_count, u64 offset, u64 *out_count);
Result dmntchtGetCheatById(DmntCheatEntry *out_cheat, u32 cheat_id);
Result dmntchtToggleCheat(u32 cheat_id);
Result dmntchtAddCheat(DmntCheatDefinition *cheat, bool enabled, u32 *out_cheat_id);
Result dmntchtRemoveCheat(u32 cheat_id);
Result dmntchtReadStaticRegister(u64 *out, u8 which);
Result dmntchtWriteStaticRegister(u8 which, u64 value);
Result dmntchtResetStaticRegisters();
Result dmntchtSetMasterCheat(DmntCheatDefinition *cheat);
Result dmntchtGetFrozenAddressCount(u64 *out_count);
Result dmntchtGetFrozenAddresses(DmntFrozenAddressEntry *buffer, u64 max_count, u64 offset, u64 *out_count);
Result dmntchtGetFrozenAddress(DmntFrozenAddressEntry *out, u64 address);
Result dmntchtEnableFrozenAddress(u64 address, u64 width, u64 *out_value);
Result dmntchtDisableFrozenAddress(u64 address);
#ifdef __cplusplus
}
#endif

View File

@@ -1,207 +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 "dmntcht.h"
static Service g_dmntchtSrv;
NX_GENERATE_SERVICE_GUARD(dmntcht);
Result _dmntchtInitialize(void) {
return smGetService(&g_dmntchtSrv, "dmnt:cht");
}
void _dmntchtCleanup(void) {
serviceClose(&g_dmntchtSrv);
}
Service* dmntchtGetServiceSession(void) {
return &g_dmntchtSrv;
}
Result dmntchtHasCheatProcess(bool *out) {
u8 tmp;
Result rc = serviceDispatchOut(&g_dmntchtSrv, 65000, tmp);
if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
return rc;
}
Result dmntchtGetCheatProcessEvent(Event *event) {
Handle evt_handle;
Result rc = serviceDispatch(&g_dmntchtSrv, 65001,
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
.out_handles = &evt_handle,
);
if (R_SUCCEEDED(rc)) {
eventLoadRemote(event, evt_handle, true);
}
return rc;
}
Result dmntchtGetCheatProcessMetadata(DmntCheatProcessMetadata *out_metadata) {
return serviceDispatchOut(&g_dmntchtSrv, 65002, *out_metadata);
}
static Result _dmntchtCmdVoid(Service* srv, u32 cmd_id) {
return serviceDispatch(srv, cmd_id);
}
Result dmntchtForceOpenCheatProcess(void) {
return _dmntchtCmdVoid(&g_dmntchtSrv, 65003);
}
Result dmntchtPauseCheatProcess(void) {
return _dmntchtCmdVoid(&g_dmntchtSrv, 65004);
}
Result dmntchtResumeCheatProcess(void) {
return _dmntchtCmdVoid(&g_dmntchtSrv, 65005);
}
Result dmntchtForceCloseCheatProcess(void) {
return _dmntchtCmdVoid(&g_dmntchtSrv, 65006);
}
static Result _dmntchtGetCount(u64 *out_count, u32 cmd_id) {
return serviceDispatchOut(&g_dmntchtSrv, cmd_id, *out_count);
}
static Result _dmntchtGetEntries(void *buffer, u64 buffer_size, u64 offset, u64 *out_count, u32 cmd_id) {
return serviceDispatchInOut(&g_dmntchtSrv, cmd_id, offset, *out_count,
.buffer_attrs = { SfBufferAttr_Out | SfBufferAttr_HipcMapAlias },
.buffers = { { buffer, buffer_size } },
);
}
static Result _dmntchtCmdInU32NoOut(u32 in, u32 cmd_id) {
return serviceDispatchIn(&g_dmntchtSrv, cmd_id, in);
}
Result dmntchtGetCheatProcessMappingCount(u64 *out_count) {
return _dmntchtGetCount(out_count, 65100);
}
Result dmntchtGetCheatProcessMappings(MemoryInfo *buffer, u64 max_count, u64 offset, u64 *out_count) {
return _dmntchtGetEntries(buffer, sizeof(*buffer) * max_count, offset, out_count, 65101);
}
Result dmntchtReadCheatProcessMemory(u64 address, void *buffer, size_t size) {
const struct {
u64 address;
u64 size;
} in = { address, size };
return serviceDispatchIn(&g_dmntchtSrv, 65102, in,
.buffer_attrs = { SfBufferAttr_Out | SfBufferAttr_HipcMapAlias },
.buffers = { { buffer, size } },
);
}
Result dmntchtWriteCheatProcessMemory(u64 address, const void *buffer, size_t size) {
const struct {
u64 address;
u64 size;
} in = { address, size };
return serviceDispatchIn(&g_dmntchtSrv, 65103, in,
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias },
.buffers = { { buffer, size } },
);
}
Result dmntchtQueryCheatProcessMemory(MemoryInfo *mem_info, u64 address){
return serviceDispatchInOut(&g_dmntchtSrv, 65104, address, *mem_info);
}
Result dmntchtGetCheatCount(u64 *out_count) {
return _dmntchtGetCount(out_count, 65200);
}
Result dmntchtGetCheats(DmntCheatEntry *buffer, u64 max_count, u64 offset, u64 *out_count) {
return _dmntchtGetEntries(buffer, sizeof(*buffer) * max_count, offset, out_count, 65201);
}
Result dmntchtGetCheatById(DmntCheatEntry *out, u32 cheat_id) {
return serviceDispatchIn(&g_dmntchtSrv, 65202, cheat_id,
.buffer_attrs = { SfBufferAttr_Out | SfBufferAttr_HipcMapAlias | SfBufferAttr_FixedSize },
.buffers = { { out, sizeof(*out) } },
);
}
Result dmntchtToggleCheat(u32 cheat_id) {
return _dmntchtCmdInU32NoOut(cheat_id, 65203);
}
Result dmntchtAddCheat(DmntCheatDefinition *cheat_def, bool enabled, u32 *out_cheat_id) {
const u8 in = enabled != 0;
return serviceDispatchInOut(&g_dmntchtSrv, 65204, in, *out_cheat_id,
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias | SfBufferAttr_FixedSize },
.buffers = { { cheat_def, sizeof(*cheat_def) } },
);
}
Result dmntchtRemoveCheat(u32 cheat_id) {
return _dmntchtCmdInU32NoOut(cheat_id, 65205);
}
Result dmntchtReadStaticRegister(u64 *out, u8 which) {
return serviceDispatchInOut(&g_dmntchtSrv, 65206, which, *out);
}
Result dmntchtWriteStaticRegister(u8 which, u64 value) {
const struct {
u64 which;
u64 value;
} in = { which, value };
return serviceDispatchIn(&g_dmntchtSrv, 65207, in);
}
Result dmntchtResetStaticRegisters() {
return _dmntchtCmdVoid(&g_dmntchtSrv, 65208);
}
Result dmntchtSetMasterCheat(DmntCheatDefinition *cheat_def) {
return serviceDispatch(&g_dmntchtSrv, 65209,
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias | SfBufferAttr_FixedSize },
.buffers = { { cheat_def, sizeof(*cheat_def) } },
);
}
Result dmntchtGetFrozenAddressCount(u64 *out_count) {
return _dmntchtGetCount(out_count, 65300);
}
Result dmntchtGetFrozenAddresses(DmntFrozenAddressEntry *buffer, u64 max_count, u64 offset, u64 *out_count) {
return _dmntchtGetEntries(buffer, sizeof(*buffer) * max_count, offset, out_count, 65301);
}
Result dmntchtGetFrozenAddress(DmntFrozenAddressEntry *out, u64 address) {
return serviceDispatchInOut(&g_dmntchtSrv, 65302, address, *out);
}
Result dmntchtEnableFrozenAddress(u64 address, u64 width, u64 *out_value) {
const struct {
u64 address;
u64 width;
} in = { address, width };
return serviceDispatchInOut(&g_dmntchtSrv, 65303, in, *out_value);
}
Result dmntchtDisableFrozenAddress(u64 address) {
return serviceDispatchIn(&g_dmntchtSrv, 65304, address);
}

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/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::erpt::srv {
extern lmem::HeapHandle g_heap_handle;
class Allocator {
public:
void *operator new(size_t sz) noexcept { return lmem::AllocateFromExpHeap(g_heap_handle, sz); }
void *operator new(size_t sz, size_t algn) noexcept { return lmem::AllocateFromExpHeap(g_heap_handle, sz, static_cast<s32>(algn)); }
void *operator new[](size_t sz) noexcept { return lmem::AllocateFromExpHeap(g_heap_handle, sz); }
void *operator new[](size_t sz, size_t algn) noexcept { return lmem::AllocateFromExpHeap(g_heap_handle, sz, static_cast<s32>(algn)); }
void operator delete(void *p) noexcept { lmem::FreeToExpHeap(g_heap_handle, p); }
void operator delete[](void *p) noexcept { lmem::FreeToExpHeap(g_heap_handle, p); }
};
inline void *Allocate(size_t sz) {
return lmem::AllocateFromExpHeap(g_heap_handle, sz);
}
inline void *AllocateWithAlign(size_t sz, size_t align) {
return lmem::AllocateFromExpHeap(g_heap_handle, sz, align);
}
inline void Deallocate(void *p) {
return lmem::FreeToExpHeap(g_heap_handle, p);
}
inline void DeallocateWithSize(void *p, size_t size) {
AMS_UNUSED(size);
return lmem::FreeToExpHeap(g_heap_handle, p);
}
}

View File

@@ -1,84 +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 "erpt_srv_attachment_impl.hpp"
#include "erpt_srv_attachment.hpp"
namespace ams::erpt::srv {
AttachmentFileName Attachment::FileName(AttachmentId attachment_id) {
char uuid_str[AttachmentFileNameLength];
attachment_id.uuid.ToString(uuid_str, sizeof(uuid_str));
AttachmentFileName attachment_name;
util::SNPrintf(attachment_name.name, sizeof(attachment_name.name), "%s:/%s.att", ReportStoragePath, uuid_str);
return attachment_name;
}
Attachment::Attachment(JournalRecord<AttachmentInfo> *r) : m_record(r) {
m_record->AddReference();
}
Attachment::~Attachment() {
this->CloseStream();
if (m_record->RemoveReference()) {
this->DeleteStream(this->FileName().name);
delete m_record;
}
}
AttachmentFileName Attachment::FileName() const {
return FileName(m_record->m_info.attachment_id);
}
Result Attachment::Open(AttachmentOpenType type) {
switch (type) {
case AttachmentOpenType_Create: R_RETURN(this->OpenStream(this->FileName().name, StreamMode_Write, AttachmentStreamBufferSize));
case AttachmentOpenType_Read: R_RETURN(this->OpenStream(this->FileName().name, StreamMode_Read, AttachmentStreamBufferSize));
default: R_THROW(erpt::ResultInvalidArgument());
}
}
Result Attachment::Read(u32 *out_read_count, u8 *dst, u32 dst_size) {
R_RETURN(this->ReadStream(out_read_count, dst, dst_size));
}
Result Attachment::Delete() {
R_RETURN(this->DeleteStream(this->FileName().name));
}
void Attachment::Close() {
return this->CloseStream();
}
Result Attachment::GetFlags(AttachmentFlagSet *out) const {
*out = m_record->m_info.flags;
R_SUCCEED();
}
Result Attachment::SetFlags(AttachmentFlagSet flags) {
if (((~m_record->m_info.flags) & flags).IsAnySet()) {
m_record->m_info.flags |= flags;
R_RETURN(Journal::Commit());
}
R_SUCCEED();
}
Result Attachment::GetSize(s64 *out) const {
R_RETURN(this->GetStreamSize(out));
}
}

View File

@@ -1,62 +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 "erpt_srv_allocator.hpp"
#include "erpt_srv_stream.hpp"
#include "erpt_srv_journal.hpp"
namespace ams::erpt::srv {
enum AttachmentOpenType {
AttachmentOpenType_Create = 0,
AttachmentOpenType_Read = 1,
};
constexpr inline u32 AttachmentStreamBufferSize = 1_KB;
class Attachment : public Allocator, public Stream {
private:
JournalRecord<AttachmentInfo> *m_record;
private:
AttachmentFileName FileName() const;
public:
static AttachmentFileName FileName(AttachmentId attachment_id);
public:
explicit Attachment(JournalRecord<AttachmentInfo> *r);
~Attachment();
Result Open(AttachmentOpenType type);
Result Read(u32 *out_read_count, u8 *dst, u32 dst_size);
Result Delete();
void Close();
Result GetFlags(AttachmentFlagSet *out) const;
Result SetFlags(AttachmentFlagSet flags);
Result GetSize(s64 *out) const;
template<typename T>
Result Write(T val) {
R_RETURN(this->WriteStream(reinterpret_cast<const u8 *>(std::addressof(val)), sizeof(val)));
}
template<typename T>
Result Write(const T *buf, u32 buffer_size) {
R_RETURN(this->WriteStream(reinterpret_cast<const u8 *>(buf), buffer_size));
}
};
}

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 "erpt_srv_attachment_impl.hpp"
#include "erpt_srv_attachment.hpp"
namespace ams::erpt::srv {
AttachmentImpl::AttachmentImpl() : m_attachment(nullptr) {
/* ... */
}
AttachmentImpl::~AttachmentImpl() {
R_ABORT_UNLESS(this->Close());
}
Result AttachmentImpl::Open(const AttachmentId &attachment_id) {
R_UNLESS(m_attachment == nullptr, erpt::ResultAlreadyInitialized());
JournalRecord<AttachmentInfo> *record = Journal::Retrieve(attachment_id);
R_UNLESS(record != nullptr, erpt::ResultNotFound());
m_attachment = new Attachment(record);
R_UNLESS(m_attachment != nullptr, erpt::ResultOutOfMemory());
auto attachment_guard = SCOPE_GUARD { delete m_attachment; m_attachment = nullptr; };
R_TRY(m_attachment->Open(AttachmentOpenType_Read));
attachment_guard.Cancel();
R_SUCCEED();
}
Result AttachmentImpl::Read(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buffer) {
R_UNLESS(m_attachment != nullptr, erpt::ResultNotInitialized());
R_RETURN(m_attachment->Read(out_count.GetPointer(), static_cast<u8 *>(out_buffer.GetPointer()), static_cast<u32>(out_buffer.GetSize())));
}
Result AttachmentImpl::SetFlags(AttachmentFlagSet flags) {
R_UNLESS(m_attachment != nullptr, erpt::ResultNotInitialized());
R_RETURN(m_attachment->SetFlags(flags));
}
Result AttachmentImpl::GetFlags(ams::sf::Out<AttachmentFlagSet> out) {
R_UNLESS(m_attachment != nullptr, erpt::ResultNotInitialized());
R_RETURN(m_attachment->GetFlags(out.GetPointer()));
}
Result AttachmentImpl::Close() {
if (m_attachment != nullptr) {
m_attachment->Close();
delete m_attachment;
m_attachment = nullptr;
}
R_SUCCEED();
}
Result AttachmentImpl::GetSize(ams::sf::Out<s64> out) {
R_UNLESS(m_attachment != nullptr, erpt::ResultNotInitialized());
R_RETURN(m_attachment->GetSize(out.GetPointer()));
}
}

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::erpt::srv {
class Attachment;
class AttachmentImpl {
private:
Attachment *m_attachment;
public:
AttachmentImpl();
~AttachmentImpl();
public:
Result Open(const AttachmentId &attachment_id);
Result Read(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buffer);
Result SetFlags(AttachmentFlagSet flags);
Result GetFlags(ams::sf::Out<AttachmentFlagSet> out);
Result Close();
Result GetSize(ams::sf::Out<s64> out);
};
static_assert(erpt::sf::IsIAttachment<AttachmentImpl>);
}

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