Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -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 } },
|
||||
);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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?");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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. */
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 ¶ms, SendHeader send_header, SendData send_data);
|
||||
|
||||
}
|
||||
@@ -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 ¶ms, 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 ¶ms, 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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. */
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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. */
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 */
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user