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

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

View File

@@ -1,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/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::pgl {
#if defined(ATMOSPHERE_OS_HORIZON)
class RemoteEventObserver {
NON_COPYABLE(RemoteEventObserver);
NON_MOVEABLE(RemoteEventObserver);
private:
::PglEventObserver m_observer;
public:
constexpr RemoteEventObserver(const ::PglEventObserver &o) : m_observer(o) { /* ... */ }
~RemoteEventObserver() {
::pglEventObserverClose(std::addressof(m_observer));
}
Result GetProcessEventHandle(ams::sf::OutCopyHandle out) {
::Event ev;
ON_RESULT_SUCCESS { out.SetValue(ev.revent, true); };
R_RETURN(::pglEventObserverGetProcessEvent(std::addressof(m_observer), std::addressof(ev)));
}
Result GetProcessEventInfo(ams::sf::Out<pm::ProcessEventInfo> out) {
static_assert(sizeof(*out.GetPointer()) == sizeof(::PmProcessEventInfo));
R_RETURN(::pglEventObserverGetProcessEventInfo(std::addressof(m_observer), reinterpret_cast<::PmProcessEventInfo *>(out.GetPointer())));
}
Result GetProcessEventHandle(ams::tipc::OutCopyHandle out) {
::Event ev;
ON_RESULT_SUCCESS { out.SetValue(ev.revent); };
R_RETURN(::pglEventObserverGetProcessEvent(std::addressof(m_observer), std::addressof(ev)));
}
Result GetProcessEventInfo(ams::tipc::Out<pm::ProcessEventInfo> out) {
static_assert(sizeof(*out.GetPointer()) == sizeof(::PmProcessEventInfo));
R_RETURN(::pglEventObserverGetProcessEventInfo(std::addressof(m_observer), reinterpret_cast<::PmProcessEventInfo *>(out.GetPointer())));
}
};
static_assert(pgl::sf::IsIEventObserver<RemoteEventObserver>);
static_assert(pgl::tipc::IsIEventObserver<RemoteEventObserver>);
#endif
}

View File

@@ -1,208 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 "pgl_remote_event_observer.hpp"
namespace ams::pgl {
namespace {
struct PglEventObserverAllocator;
using RemoteAllocator = ams::sf::ExpHeapStaticAllocator<1_KB, PglEventObserverAllocator>;
using RemoteObjectFactory = ams::sf::ObjectFactory<typename RemoteAllocator::Policy>;
class StaticAllocatorInitializer {
public:
StaticAllocatorInitializer() {
RemoteAllocator::Initialize(lmem::CreateOption_None);
}
} g_static_allocator_initializer;
template<typename T, typename... Args>
T *AllocateFromStaticExpHeap(Args &&... args) {
T * const object = static_cast<T *>(RemoteAllocator::Allocate(sizeof(T)));
if (AMS_LIKELY(object != nullptr)) {
std::construct_at(object, std::forward<Args>(args)...);
}
return object;
}
template<typename T>
void FreeToStaticExpHeap(T *object) {
return RemoteAllocator::Deallocate(object, sizeof(T));
}
template<typename T, typename... Args> requires std::derived_from<T, impl::EventObserverInterface>
EventObserver::UniquePtr MakeUniqueFromStaticExpHeap(Args &&... args) {
return EventObserver::UniquePtr{AllocateFromStaticExpHeap<T>(std::forward<Args>(args)...)};
}
}
void EventObserver::Deleter::operator()(impl::EventObserverInterface *obj) {
FreeToStaticExpHeap(obj);
}
#if defined(ATMOSPHERE_OS_HORIZON)
Result Initialize() {
R_RETURN(::pglInitialize());
}
void Finalize() {
return pglExit();
}
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 process_flags, u8 pgl_flags) {
static_assert(sizeof(*out) == sizeof(u64));
static_assert(sizeof(loc) == sizeof(::NcmProgramLocation));
R_RETURN(::pglLaunchProgram(reinterpret_cast<u64 *>(out), reinterpret_cast<const ::NcmProgramLocation *>(std::addressof(loc)), process_flags, pgl_flags));
}
Result TerminateProcess(os::ProcessId process_id) {
R_RETURN(::pglTerminateProcess(static_cast<u64>(process_id)));
}
Result LaunchProgramFromHost(os::ProcessId *out, const char *content_path, u32 process_flags) {
static_assert(sizeof(*out) == sizeof(u64));
R_RETURN(::pglLaunchProgramFromHost(reinterpret_cast<u64 *>(out), content_path, process_flags));
}
Result GetHostContentMetaInfo(pgl::ContentMetaInfo *out, const char *content_path) {
static_assert(sizeof(*out) == sizeof(::PglContentMetaInfo));
R_RETURN(::pglGetHostContentMetaInfo(reinterpret_cast<::PglContentMetaInfo *>(out), content_path));
}
Result GetApplicationProcessId(os::ProcessId *out) {
static_assert(sizeof(*out) == sizeof(u64));
R_RETURN(::pglGetApplicationProcessId(reinterpret_cast<u64 *>(out)));
}
Result BoostSystemMemoryResourceLimit(u64 size) {
R_RETURN(::pglBoostSystemMemoryResourceLimit(size));
}
Result IsProcessTracked(bool *out, os::ProcessId process_id) {
R_RETURN(::pglIsProcessTracked(out, static_cast<u64>(process_id)));
}
Result EnableApplicationCrashReport(bool enabled) {
R_RETURN(::pglEnableApplicationCrashReport(enabled));
}
Result IsApplicationCrashReportEnabled(bool *out) {
R_RETURN(::pglIsApplicationCrashReportEnabled(out));
}
Result EnableApplicationAllThreadDumpOnCrash(bool enabled) {
R_RETURN(::pglEnableApplicationAllThreadDumpOnCrash(enabled));
}
Result TriggerApplicationSnapShotDumper(const char *arg, SnapShotDumpType dump_type) {
R_RETURN(::pglTriggerApplicationSnapShotDumper(static_cast<::PglSnapShotDumpType>(dump_type), arg));
}
Result GetEventObserver(pgl::EventObserver *out) {
::PglEventObserver obs;
R_TRY(::pglGetEventObserver(std::addressof(obs)));
if (hos::GetVersion() >= hos::Version_12_0_0) {
auto observer_holder = MakeUniqueFromStaticExpHeap<impl::EventObserverByTipc<RemoteEventObserver>>(obs);
R_UNLESS(observer_holder != nullptr, pgl::ResultOutOfMemory());
*out = pgl::EventObserver(std::move(observer_holder));
} else {
auto remote_observer = RemoteObjectFactory::CreateSharedEmplaced<pgl::sf::IEventObserver, RemoteEventObserver>(obs);
R_UNLESS(remote_observer != nullptr, pgl::ResultOutOfMemory());
auto observer_holder = MakeUniqueFromStaticExpHeap<impl::EventObserverByCmif>(std::move(remote_observer));
R_UNLESS(observer_holder != nullptr, pgl::ResultOutOfMemory());
*out = pgl::EventObserver(std::move(observer_holder));
}
R_SUCCEED();
}
#else
Result Initialize() {
AMS_ABORT("TODO");
}
void Finalize() {
AMS_ABORT("TODO");
}
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 process_flags, u8 pgl_flags) {
AMS_UNUSED(out, loc, process_flags, pgl_flags);
AMS_ABORT("TODO");
}
Result TerminateProcess(os::ProcessId process_id) {
AMS_UNUSED(process_id);
AMS_ABORT("TODO");
}
Result LaunchProgramFromHost(os::ProcessId *out, const char *content_path, u32 process_flags) {
AMS_UNUSED(out, content_path, process_flags);
AMS_ABORT("TODO");
}
Result GetHostContentMetaInfo(pgl::ContentMetaInfo *out, const char *content_path) {
AMS_UNUSED(out, content_path);
AMS_ABORT("TODO");
}
Result GetApplicationProcessId(os::ProcessId *out) {
AMS_UNUSED(out);
AMS_ABORT("TODO");
}
Result BoostSystemMemoryResourceLimit(u64 size) {
AMS_UNUSED(size);
AMS_ABORT("TODO");
}
Result IsProcessTracked(bool *out, os::ProcessId process_id) {
AMS_UNUSED(out, process_id);
AMS_ABORT("TODO");
}
Result EnableApplicationCrashReport(bool enabled) {
AMS_UNUSED(enabled);
AMS_ABORT("TODO");
}
Result IsApplicationCrashReportEnabled(bool *out) {
AMS_UNUSED(out);
AMS_ABORT("TODO");
}
Result EnableApplicationAllThreadDumpOnCrash(bool enabled) {
AMS_UNUSED(enabled);
AMS_ABORT("TODO");
}
Result TriggerApplicationSnapShotDumper(const char *arg, SnapShotDumpType dump_type) {
AMS_UNUSED(arg, dump_type);
AMS_ABORT("TODO");
}
Result GetEventObserver(pgl::EventObserver *out) {
AMS_UNUSED(out);
AMS_ABORT("TODO");
}
#endif
}

View File

@@ -1,207 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pgl_srv_shell.hpp"
#include "pgl_srv_shell_event_observer.hpp"
#include "pgl_srv_tipc_utils.hpp"
namespace ams::pgl::srv {
namespace {
/* pgl. */
enum PortIndex {
PortIndex_Shell,
PortIndex_Count,
};
constexpr sm::ServiceName ShellServiceName = sm::ServiceName::Encode("pgl");
constexpr size_t ShellMaxSessions = 8; /* Official maximum is 6. */
using CmifServerManager = ams::sf::hipc::ServerManager<PortIndex_Count>;
constexpr size_t ObserverMaxSessions = 4;
using ShellPortMeta = ams::tipc::PortMeta<ShellMaxSessions + ObserverMaxSessions, pgl::tipc::IShellInterface, pgl::srv::ShellInterfaceTipc, ams::tipc::SingletonAllocator>;
using TipcServerManager = ams::tipc::ServerManager<ShellPortMeta>;
/* NOTE: Nintendo reserves only 0x2000 bytes for heap, which is used "mostly" to allocate shell event observers. */
/* However, we would like very much for homebrew sysmodules to be able to subscribe to events if they so choose */
/* And so we will use a larger heap (32 KB). Note that we reduce the heap size for tipc, where objects are */
/* allocated statically. */
/* We should have a smaller memory footprint than N in the end, regardless. */
struct CmifGlobals {
u8 heap_memory[32_KB];
lmem::HeapHandle heap_handle;
ams::sf::ExpHeapAllocator server_allocator;
ams::sf::UnmanagedServiceObject<pgl::sf::IShellInterface, pgl::srv::ShellInterfaceCmif> shell_interface{std::addressof(server_allocator)};
CmifServerManager server_manager;
};
struct TipcGlobals {
u8 heap_memory[24_KB];
lmem::HeapHandle heap_handle;
TipcServerManager server_manager;
ams::tipc::SlabAllocator<ams::tipc::ServiceObject<pgl::tipc::IEventObserver, pgl::srv::ShellEventObserverTipc>, ObserverMaxSessions> observer_allocator;
};
constinit union {
util::TypedStorage<CmifGlobals> cmif;
util::TypedStorage<TipcGlobals> tipc;
} g_globals;
ALWAYS_INLINE CmifGlobals &GetGlobalsForCmif() {
return GetReference(g_globals.cmif);
}
ALWAYS_INLINE TipcGlobals &GetGlobalsForTipc() {
return GetReference(g_globals.tipc);
}
ALWAYS_INLINE bool UseTipcServer() {
return hos::GetVersion() >= hos::Version_12_0_0;
}
ALWAYS_INLINE lmem::HeapHandle GetHeapHandle() {
if (UseTipcServer()) {
return GetGlobalsForTipc().heap_handle;
} else {
return GetGlobalsForCmif().heap_handle;
}
}
template<typename T>
ALWAYS_INLINE void InitializeHeapImpl(util::TypedStorage<T> &globals_storage) {
/* Construct the globals object. */
util::ConstructAt(globals_storage);
/* Get reference to the globals. */
auto &globals = GetReference(globals_storage);
/* Set the heap handle. */
globals.heap_handle = lmem::CreateExpHeap(globals.heap_memory, sizeof(globals.heap_memory), lmem::CreateOption_ThreadSafe);
/* If we should, setup the server allocator. */
if constexpr (requires (T &t) { t.server_allocator; }) {
globals.server_allocator.Attach(globals.heap_handle);
}
}
void RegisterServiceSession() {
/* Register "pgl" with the appropriate server manager. */
if (UseTipcServer()) {
/* Get the globals. */
auto &globals = GetGlobalsForTipc();
/* Initialize the server manager. */
globals.server_manager.Initialize();
/* Register the pgl service. */
globals.server_manager.RegisterPort(ShellServiceName, ShellMaxSessions);
} else {
/* Get the globals. */
auto &globals = GetGlobalsForCmif();
/* Register the shell server with the cmif server manager. */
R_ABORT_UNLESS(globals.server_manager.RegisterObjectForServer(globals.shell_interface.GetShared(), ShellServiceName, ShellMaxSessions));
}
}
void LoopProcessServer() {
/* Loop processing for the appropriate server manager. */
if (UseTipcServer()) {
GetGlobalsForTipc().server_manager.LoopAuto();
} else {
GetGlobalsForCmif().server_manager.LoopProcess();
}
}
}
void InitializeHeap() {
/* Initialize the heap (and construct the globals object) for the appropriate ipc protocol. */
if (UseTipcServer()) {
/* We're servicing via tipc. */
InitializeHeapImpl(g_globals.tipc);
} else {
/* We're servicing via cmif. */
InitializeHeapImpl(g_globals.cmif);
}
}
void *Allocate(size_t size) {
return lmem::AllocateFromExpHeap(GetHeapHandle(), size);
}
void Deallocate(void *p, size_t size) {
AMS_UNUSED(size);
return lmem::FreeToExpHeap(GetHeapHandle(), p);
}
void StartServer() {
/* Enable extra application threads, if we should. */
{
u8 enable_application_extra_thread;
const size_t sz = settings::fwdbg::GetSettingsItemValue(std::addressof(enable_application_extra_thread), sizeof(enable_application_extra_thread), "application_extra_thread", "enable_application_extra_thread");
if (sz == sizeof(enable_application_extra_thread) && enable_application_extra_thread != 0) {
/* NOTE: Nintendo does not check that this succeeds. */
pm::shell::BoostApplicationThreadResourceLimit();
}
}
/* Enable extra system threads, if we should. */
{
u8 enable_system_extra_thread;
const size_t sz = settings::fwdbg::GetSettingsItemValue(std::addressof(enable_system_extra_thread), sizeof(enable_system_extra_thread), "application_extra_thread", "enable_system_extra_thread");
if (sz == sizeof(enable_system_extra_thread) && enable_system_extra_thread != 0) {
/* NOTE: Nintendo does not check that this succeeds. */
pm::shell::BoostSystemThreadResourceLimit();
}
}
/* Register service session. */
RegisterServiceSession();
/* Start the Process Tracking thread. */
pgl::srv::InitializeProcessControlTask();
/* Loop process. */
LoopProcessServer();
}
Result AllocateShellEventObserverForTipc(os::NativeHandle *out) {
/* Get the shell event observer allocator. */
auto &allocator = GetGlobalsForTipc().observer_allocator;
/* Allocate an object. */
auto *object = allocator.Allocate();
R_UNLESS(object != nullptr, pgl::ResultOutOfMemory());
/* Set the object's deleter. */
object->SetDeleter(std::addressof(allocator));
/* Add the session to the server manager. */
/* NOTE: If this fails, the object will be leaked. */
/* TODO: Should we avoid leaking the object? Nintendo does not. */
R_TRY(GetGlobalsForTipc().server_manager.AddSession(out, object));
R_SUCCEED();
}
}

View File

@@ -1,499 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 "pgl_srv_shell.hpp"
#include "pgl_srv_shell_event_observer.hpp"
namespace ams::pgl::srv {
namespace {
constexpr inline size_t ProcessDataCount = 0x20;
struct ProcessData {
os::ProcessId process_id;
u32 flags;
};
static_assert(util::is_pod<ProcessData>::value);
enum ProcessDataFlag : u32 {
ProcessDataFlag_None = 0,
ProcessDataFlag_DetailedCrashReportAllowed = (1 << 0),
ProcessDataFlag_DetailedCrashReportEnabled = (1 << 1),
ProcessDataFlag_HasLogOption = (1 << 2),
ProcessDataFlag_OutputAllLog = (1 << 3),
ProcessDataFlag_EnableCrashReportScreenShot = (1 << 4),
};
constinit bool g_is_production = true;
constinit bool g_enable_crash_report_screenshot = true;
constinit bool g_enable_jit_debug = false;
constexpr inline size_t ProcessControlTaskStackSize = 8_KB;
constinit os::ThreadType g_process_control_task_thread = {};
alignas(os::ThreadStackAlignment) constinit u8 g_process_control_task_stack[ProcessControlTaskStackSize];
constinit os::SdkMutex g_observer_list_mutex;
constinit util::IntrusiveListBaseTraits<ShellEventObserverHolder>::ListType g_observer_list;
constinit os::SdkMutex g_process_data_mutex;
constinit ProcessData g_process_data[ProcessDataCount] = {};
constinit os::ProcessId g_crashed_process_id = os::InvalidProcessId;
constinit os::ProcessId g_creport_process_id = os::InvalidProcessId;
constinit os::ProcessId g_ssd_process_id = os::InvalidProcessId;
ProcessData *FindProcessData(os::ProcessId process_id) {
for (auto &data : g_process_data) {
if (data.process_id == process_id) {
return std::addressof(data);
}
}
return nullptr;
}
u32 ConvertToProcessDataFlags(u8 pgl_flags) {
if ((pgl_flags & pgl::LaunchFlags_EnableDetailedCrashReport) == 0) {
/* If we shouldn't generate detailed crash reports, set no flags. */
return ProcessDataFlag_None;
} else {
/* We can and should generate detailed crash reports. */
u32 data_flags = ProcessDataFlag_DetailedCrashReportAllowed | ProcessDataFlag_DetailedCrashReportEnabled;
/* If we should enable crash report screenshots, check the correct flag. */
if (g_enable_crash_report_screenshot) {
const u32 test_flag = g_is_production ? pgl::LaunchFlags_EnableCrashReportScreenShotForProduction : pgl::LaunchFlags_EnableCrashReportScreenShotForDevelop;
if ((pgl_flags & test_flag) != 0) {
data_flags |= ProcessDataFlag_EnableCrashReportScreenShot;
}
}
return data_flags;
}
}
util::optional<os::ProcessId> GetRunningApplicationProcessId() {
os::ProcessId process_id;
if (R_SUCCEEDED(pm::shell::GetApplicationProcessIdForShell(std::addressof(process_id)))) {
return process_id;
} else {
return util::nullopt;
}
}
s32 ConvertDumpTypeToArgument(SnapShotDumpType dump_type) {
switch (dump_type) {
case SnapShotDumpType::None: return -1;
case SnapShotDumpType::Auto: return 0;
case SnapShotDumpType::Full: return 1;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
bool GetSnapShotDumpOutputAllLog(os::ProcessId process_id) {
/* Check if we have an option set for the process. */
{
std::scoped_lock lk(g_process_data_mutex);
if (ProcessData *data = FindProcessData(process_id); data != nullptr) {
if ((data->flags & ProcessDataFlag_HasLogOption) != 0) {
return ((data->flags & ProcessDataFlag_OutputAllLog) != 0);
}
}
}
/* If we don't have an option for the process, fall back to settings. */
u8 log_option;
const size_t option_size = settings::fwdbg::GetSettingsItemValue(std::addressof(log_option), sizeof(log_option), "snap_shot_dump", "output_all_log");
return (option_size == sizeof(log_option) && log_option != 0);
}
size_t CreateSnapShotDumpArguments(char *dst, size_t dst_size, os::ProcessId process_id, SnapShotDumpType dump_type, const char *str_arg) {
const s32 dump_arg = ConvertDumpTypeToArgument(dump_type);
const s32 log_arg = GetSnapShotDumpOutputAllLog(process_id) ? 1 : 0;
if (str_arg != nullptr) {
return util::SNPrintf(dst, dst_size, "D %010llu \"%s\" -log %d -dump %d", static_cast<unsigned long long>(static_cast<u64>(process_id)), str_arg, log_arg, dump_arg);
} else {
return util::SNPrintf(dst, dst_size, "D %010llu -log %d -dump %d", static_cast<unsigned long long>(static_cast<u64>(process_id)), log_arg, dump_arg);
}
}
Result TriggerSnapShotDumper(os::ProcessId process_id, SnapShotDumpType dump_type, const char *arg) {
/* Create the arguments. */
char process_arguments[800];
const size_t arg_len = CreateSnapShotDumpArguments(process_arguments, sizeof(process_arguments), process_id, dump_type, arg);
/* Set the arguments. */
R_TRY(ldr::SetProgramArgument(ncm::SystemDebugAppletId::SnapShotDumper, process_arguments, arg_len + 1));
/* Launch the process. */
os::ProcessId ssd_process_id = os::InvalidProcessId;
R_TRY(pm::shell::LaunchProgram(std::addressof(ssd_process_id), ncm::ProgramLocation::Make(ncm::SystemDebugAppletId::SnapShotDumper, ncm::StorageId::BuiltInSystem), pm::LaunchFlags_None));
/* Set the globals. */
g_crashed_process_id = process_id;
g_ssd_process_id = ssd_process_id;
R_SUCCEED();
}
bool ShouldSnapShotAutoDump() {
bool dump;
const size_t sz = settings::fwdbg::GetSettingsItemValue(std::addressof(dump), sizeof(dump), "snap_shot_dump", "auto_dump");
return sz == sizeof(dump) && dump;
}
bool ShouldSnapShotFullDump() {
bool dump;
const size_t sz = settings::fwdbg::GetSettingsItemValue(std::addressof(dump), sizeof(dump), "snap_shot_dump", "full_dump");
return sz == sizeof(dump) && dump;
}
SnapShotDumpType GetSnapShotDumpType() {
if (ShouldSnapShotAutoDump()) {
if (ShouldSnapShotFullDump()) {
return SnapShotDumpType::Full;
} else {
return SnapShotDumpType::Auto;
}
} else {
return SnapShotDumpType::None;
}
}
void TriggerSnapShotDumper(os::ProcessId process_id) {
TriggerSnapShotDumper(process_id, GetSnapShotDumpType(), nullptr);
}
s32 GetCrashReportDetailedArgument(u32 data_flags) {
if (((data_flags & ProcessDataFlag_DetailedCrashReportAllowed) != 0) && ((data_flags & ProcessDataFlag_DetailedCrashReportEnabled) != 0)) {
return 1;
} else {
return 0;
}
}
s32 GetCrashReportScreenShotArgument(u32 data_flags) {
if (settings::system::GetErrorReportSharePermission() == settings::system::ErrorReportSharePermission_Granted) {
return ((data_flags & ProcessDataFlag_EnableCrashReportScreenShot) != 0) ? 1 : 0;
} else {
return 0;
}
}
void TriggerCrashReport(os::ProcessId process_id) {
/* If the program that crashed is creport, we should just terminate both processes and return. */
if (process_id == g_creport_process_id) {
TerminateProcess(g_crashed_process_id);
TerminateProcess(g_creport_process_id);
g_crashed_process_id = os::InvalidProcessId;
g_creport_process_id = os::InvalidProcessId;
return;
}
/* Get the data flags for the process. */
u32 data_flags;
{
std::scoped_lock lk(g_process_data_mutex);
if (auto *data = FindProcessData(process_id); data != nullptr) {
data_flags = data->flags;
} else {
data_flags = ProcessDataFlag_None;
}
}
/* Generate arguments. */
char arguments[0x40];
const size_t len = util::SNPrintf(arguments, sizeof(arguments), "%" PRId64 " %d %d %d", static_cast<s64>(static_cast<u64>(process_id)), GetCrashReportDetailedArgument(data_flags), GetCrashReportScreenShotArgument(data_flags), g_enable_jit_debug);
if (R_FAILED(ldr::SetProgramArgument(ncm::SystemProgramId::Creport, arguments, len + 1))) {
return;
}
/* Launch creport. */
os::ProcessId creport_process_id;
if (R_FAILED(pm::shell::LaunchProgram(std::addressof(creport_process_id), ncm::ProgramLocation::Make(ncm::SystemProgramId::Creport, ncm::StorageId::BuiltInSystem), pm::LaunchFlags_None))) {
return;
}
/* Set the globals. */
g_crashed_process_id = process_id;
g_creport_process_id = creport_process_id;
}
void HandleException(os::ProcessId process_id) {
if (g_enable_jit_debug) {
/* If jit debug is enabled, we want to try to launch snap shot dumper. */
ProcessData *data = nullptr;
{
std::scoped_lock lk(g_process_data_mutex);
data = FindProcessData(process_id);
}
/* If we're tracking the process, we can launch dumper. Otherwise we should just terminate. */
if (data != nullptr) {
TriggerSnapShotDumper(process_id);
} else {
TerminateProcess(process_id);
}
} else {
/* Otherwise, we want to launch creport. */
TriggerCrashReport(process_id);
}
}
void HandleExit(os::ProcessId process_id) {
std::scoped_lock lk(g_process_data_mutex);
if (auto *data = FindProcessData(process_id); data != nullptr) {
data->process_id = os::InvalidProcessId;
}
}
void OnProcessEvent(const pm::ProcessEventInfo &event_info) {
/* Determine if we're tracking the process. */
ProcessData *data = nullptr;
{
std::scoped_lock lk(g_process_data_mutex);
data = FindProcessData(event_info.process_id);
}
/* If we are, we're going to want to notify our listeners. */
if (data != nullptr) {
/* If we closed the process, note that. */
if (static_cast<pm::ProcessEvent>(event_info.event) == pm::ProcessEvent::Exited) {
HandleExit(event_info.process_id);
}
/* Notify all observers. */
std::scoped_lock lk(g_observer_list_mutex);
for (auto &observer : g_observer_list) {
observer.Notify(event_info);
}
}
/* If the process crashed, handle that. */
if (static_cast<pm::ProcessEvent>(event_info.event) == pm::ProcessEvent::Exception) {
HandleException(event_info.process_id);
}
}
void ProcessControlTask(void *) {
/* Get the process event event from pm. */
os::SystemEvent process_event;
R_ABORT_UNLESS(pm::shell::GetProcessEventEvent(std::addressof(process_event)));
while (true) {
/* Wait for an event to come in, and clear our signal. */
process_event.Wait();
process_event.Clear();
bool continue_getting_event = true;
while (continue_getting_event) {
/* Try to get an event info. */
pm::ProcessEventInfo event_info;
if (R_FAILED(pm::shell::GetProcessEventInfo(std::addressof(event_info)))) {
break;
}
/* Process the event. */
switch (static_cast<pm::ProcessEvent>(event_info.event)) {
case pm::ProcessEvent::None:
continue_getting_event = false;
break;
case pm::ProcessEvent::Exited:
{
/* If SnapShotDumper terminated, trigger a crash report. */
if (event_info.process_id == g_ssd_process_id && g_crashed_process_id != os::InvalidProcessId) {
TriggerCrashReport(g_crashed_process_id);
}
}
[[fallthrough]];
case pm::ProcessEvent::Started:
case pm::ProcessEvent::Exception:
case pm::ProcessEvent::DebugRunning:
case pm::ProcessEvent::DebugBreak:
OnProcessEvent(event_info);
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}
}
}
void InitializeProcessControlTask() {
/* Create the task thread. */
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_process_control_task_thread), ProcessControlTask, nullptr, g_process_control_task_stack, sizeof(g_process_control_task_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(pgl, ProcessControlTask)));
os::SetThreadNamePointer(std::addressof(g_process_control_task_thread), AMS_GET_SYSTEM_THREAD_NAME(pgl, ProcessControlTask));
/* Retrieve settings. */
settings::fwdbg::GetSettingsItemValue(std::addressof(g_enable_jit_debug), sizeof(g_enable_jit_debug), "jit_debug", "enable_jit_debug");
settings::fwdbg::GetSettingsItemValue(std::addressof(g_enable_crash_report_screenshot), sizeof(g_enable_crash_report_screenshot), "creport", "crash_screen_shot");
g_is_production = !settings::fwdbg::IsDebugModeEnabled();
/* Clear all process data. */
{
for (size_t i = 0; i < util::size(g_process_data); i++) {
g_process_data[i].process_id = os::InvalidProcessId;
}
}
/* Start the thread. */
os::StartThread(std::addressof(g_process_control_task_thread));
}
void RegisterShellEventObserver(ShellEventObserverHolder *holder) {
std::scoped_lock lk(g_observer_list_mutex);
g_observer_list.push_back(*holder);
}
void UnregisterShellEventObserver(ShellEventObserverHolder *holder) {
std::scoped_lock lk(g_observer_list_mutex);
for (auto &observer : g_observer_list) {
if (std::addressof(observer) == holder) {
g_observer_list.erase(g_observer_list.iterator_to(observer));
break;
}
}
}
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags) {
/* Convert the input flags to the internal format. */
const u32 data_flags = ConvertToProcessDataFlags(pgl_flags);
/* If jit debug is enabled, we want to be signaled on crash. */
if (g_enable_jit_debug) {
pm_flags |= pm::LaunchFlags_SignalOnException;
}
/* Launch the process. */
os::ProcessId process_id;
R_TRY(pm::shell::LaunchProgram(std::addressof(process_id), loc, pm_flags & pm::LaunchFlagsMask));
/* Create a ProcessData for the process. */
{
std::scoped_lock lk(g_process_data_mutex);
ProcessData *new_data = FindProcessData(os::InvalidProcessId);
AMS_ABORT_UNLESS(new_data != nullptr);
new_data->process_id = process_id;
new_data->flags = data_flags;
}
/* We succeeded. */
*out = process_id;
R_SUCCEED();
}
Result TerminateProcess(os::ProcessId process_id) {
/* Ask PM to terminate the process. */
R_RETURN(pm::shell::TerminateProcess(process_id));
}
Result GetApplicationProcessId(os::ProcessId *out) {
/* Get the application process id. */
auto application_process_id = GetRunningApplicationProcessId();
R_UNLESS(application_process_id, pgl::ResultApplicationNotRunning());
/* Return the id. */
*out = *application_process_id;
R_SUCCEED();
}
Result BoostSystemMemoryResourceLimit(u64 size) {
/* Ask PM to boost the limit. */
R_RETURN(pm::shell::BoostSystemMemoryResourceLimit(size));
}
bool IsProcessTracked(os::ProcessId process_id) {
/* Check whether a ProcessData exists for the process. */
std::scoped_lock lk(g_process_data_mutex);
return FindProcessData(process_id) != nullptr;
}
void EnableApplicationCrashReport(bool enabled) {
/* Get the application process id. */
auto application_process_id = GetRunningApplicationProcessId();
if (application_process_id) {
/* Find the data for the application process. */
std::scoped_lock lk(g_process_data_mutex);
ProcessData *data = FindProcessData(*application_process_id);
/* It's okay if we aren't tracking the process. */
if (data != nullptr) {
/* Set or clear the flag. */
if (enabled) {
data->flags |= ProcessDataFlag_DetailedCrashReportEnabled;
} else {
data->flags &= ~ProcessDataFlag_DetailedCrashReportEnabled;
}
}
}
}
bool IsApplicationCrashReportEnabled() {
/* Get the application process id. */
auto application_process_id = GetRunningApplicationProcessId();
if (!application_process_id) {
return false;
}
/* Find the data for the process. */
std::scoped_lock lk(g_process_data_mutex);
if (ProcessData *data = FindProcessData(*application_process_id); data != nullptr) {
return (data->flags & ProcessDataFlag_DetailedCrashReportEnabled) != 0;
} else {
return false;
}
}
void EnableApplicationAllThreadDumpOnCrash(bool enabled) {
/* Get the application process id. */
auto application_process_id = GetRunningApplicationProcessId();
if (application_process_id) {
/* Find the data for the application process. */
std::scoped_lock lk(g_process_data_mutex);
ProcessData *data = FindProcessData(*application_process_id);
/* It's okay if we aren't tracking the process. */
if (data != nullptr) {
/* Set or clear the flag. */
if (enabled) {
data->flags |= ProcessDataFlag_OutputAllLog;
} else {
data->flags &= ~ProcessDataFlag_OutputAllLog;
}
/* NOTE: Here Nintendo releases the lock, re-takes the lock, and re-finds the process data. */
/* This is unnecessary and less efficient, so we will not bother. */
/* Note that the flag bit has a meaningful value. */
data->flags |= ProcessDataFlag_HasLogOption;
}
}
}
Result TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const char *arg) {
/* Try to get the application process id. */
os::ProcessId process_id;
R_TRY(pm::shell::GetApplicationProcessIdForShell(std::addressof(process_id)));
/* Launch the snapshot dumper, clearing the global tracker process id. */
ON_SCOPE_EXIT { g_ssd_process_id = os::InvalidProcessId; };
R_RETURN(TriggerSnapShotDumper(process_id, dump_type, arg));
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::pgl::srv {
void InitializeProcessControlTask();
class ShellEventObserverHolder;
void RegisterShellEventObserver(ShellEventObserverHolder *holder);
void UnregisterShellEventObserver(ShellEventObserverHolder *holder);
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags);
Result TerminateProcess(os::ProcessId process_id);
Result GetApplicationProcessId(os::ProcessId *out);
Result BoostSystemMemoryResourceLimit(u64 size);
bool IsProcessTracked(os::ProcessId process_id);
void EnableApplicationCrashReport(bool enabled);
bool IsApplicationCrashReportEnabled();
void EnableApplicationAllThreadDumpOnCrash(bool enabled);
Result TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const char *arg);
}

View File

@@ -1,86 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pgl_srv_shell_event_observer.hpp"
#include "pgl_srv_shell.hpp"
namespace ams::pgl::srv {
ShellEventObserverImpl::ShellEventObserverImpl() : m_message_queue(m_queue_buffer, QueueCapacity), m_event(os::EventClearMode_AutoClear, true) {
m_heap_handle = lmem::CreateUnitHeap(m_event_info_data, sizeof(m_event_info_data), sizeof(m_event_info_data[0]), lmem::CreateOption_ThreadSafe, 8, GetPointer(m_heap_head));
RegisterShellEventObserver(util::ConstructAt(m_holder, this));
}
ShellEventObserverImpl::~ShellEventObserverImpl() {
UnregisterShellEventObserver(GetPointer(m_holder));
util::DestroyAt(m_holder);
}
Result ShellEventObserverImpl::PopEventInfo(pm::ProcessEventInfo *out) {
/* Receive an info from the queue. */
uintptr_t info_address;
R_UNLESS(m_message_queue.TryReceive(std::addressof(info_address)), pgl::ResultNotAvailable());
pm::ProcessEventInfo *info = reinterpret_cast<pm::ProcessEventInfo *>(info_address);
/* Set the output. */
*out = *info;
/* Free the received info. */
lmem::FreeToUnitHeap(m_heap_handle, info);
R_SUCCEED();
}
void ShellEventObserverImpl::Notify(const pm::ProcessEventInfo &info) {
/* Allocate a new info. */
auto allocated = reinterpret_cast<pm::ProcessEventInfo *>(lmem::AllocateFromUnitHeap(m_heap_handle));
if (!allocated) {
return;
}
/* Set it to the notification. */
*allocated = info;
/* Try to send it. */
if (!m_message_queue.TrySend(reinterpret_cast<uintptr_t>(allocated))) {
lmem::FreeToUnitHeap(m_heap_handle, allocated);
return;
}
/* Notify that we have a new info available. */
m_event.Signal();
}
Result ShellEventObserverCmif::GetProcessEventHandle(ams::sf::OutCopyHandle out) {
out.SetValue(this->GetEvent().GetReadableHandle(), false);
R_SUCCEED();
}
Result ShellEventObserverCmif::GetProcessEventInfo(ams::sf::Out<pm::ProcessEventInfo> out) {
R_RETURN(this->PopEventInfo(out.GetPointer()));
}
Result ShellEventObserverTipc::GetProcessEventHandle(ams::tipc::OutCopyHandle out) {
out.SetValue(this->GetEvent().GetReadableHandle());
R_SUCCEED();
}
Result ShellEventObserverTipc::GetProcessEventInfo(ams::tipc::Out<pm::ProcessEventInfo> out) {
R_RETURN(this->PopEventInfo(out.GetPointer()));
}
}

View File

@@ -1,75 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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::pgl::srv {
class IShellEventObserver {
public:
virtual void Notify(const pm::ProcessEventInfo &info) = 0;
};
class ShellEventObserverHolder : public util::IntrusiveListBaseNode<ShellEventObserverHolder> {
private:
IShellEventObserver *m_observer;
public:
explicit ShellEventObserverHolder(IShellEventObserver *observer) : m_observer(observer) { /* ... */ }
void Notify(const pm::ProcessEventInfo &info) {
m_observer->Notify(info);
}
};
class ShellEventObserverImpl : public IShellEventObserver {
private:
static constexpr size_t QueueCapacity = 0x20;
private:
os::MessageQueue m_message_queue;
uintptr_t m_queue_buffer[QueueCapacity];
os::SystemEvent m_event;
util::TypedStorage<lmem::HeapCommonHead> m_heap_head;
lmem::HeapHandle m_heap_handle;
pm::ProcessEventInfo m_event_info_data[QueueCapacity];
util::TypedStorage<ShellEventObserverHolder> m_holder;
public:
ShellEventObserverImpl();
~ShellEventObserverImpl();
os::SystemEvent &GetEvent() {
return m_event;
}
Result PopEventInfo(pm::ProcessEventInfo *out);
virtual void Notify(const pm::ProcessEventInfo &info) override final;
};
class ShellEventObserverCmif : public ShellEventObserverImpl {
public:
Result GetProcessEventHandle(ams::sf::OutCopyHandle out);
Result GetProcessEventInfo(ams::sf::Out<pm::ProcessEventInfo> out);
};
static_assert(pgl::sf::IsIEventObserver<ShellEventObserverCmif>);
class ShellEventObserverTipc : public ShellEventObserverImpl {
public:
Result GetProcessEventHandle(ams::tipc::OutCopyHandle out);
Result GetProcessEventInfo(ams::tipc::Out<pm::ProcessEventInfo> out);
};
static_assert(pgl::tipc::IsIEventObserver<ShellEventObserverTipc>);
}

View File

@@ -1,357 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 "pgl_srv_shell_host_utils.hpp"
#include "pgl_srv_shell.hpp"
namespace ams::pgl::srv {
namespace {
constexpr inline char HostPackageMountName[] = "HostPackageRead";
static_assert(sizeof(HostPackageMountName) - 1 <= fs::MountNameLengthMax);
struct CaseInsensitiveCharTraits : public std::char_traits<char> {
static constexpr char to_upper(char c) {
return std::toupper(static_cast<unsigned char>(c));
}
static constexpr bool eq(char c1, char c2) {
return to_upper(c1) == to_upper(c2);
}
static constexpr bool lt(char c1, char c2) {
return to_upper(c1) < to_upper(c2);
}
static constexpr int compare(const char *s1, const char *s2, size_t n) {
while ( n-- != 0 ) {
if ( to_upper(*s1) < to_upper(*s2) ) return -1;
if ( to_upper(*s1) > to_upper(*s2) ) return 1;
++s1; ++s2;
}
return 0;
}
static constexpr const char *find(const char *s, int n, char a) {
auto const ua (to_upper(a));
while ( n-- != 0 )
{
if (to_upper(*s) == ua)
return s;
s++;
}
return nullptr;
}
};
using PathView = util::basic_string_view<char, CaseInsensitiveCharTraits>;
enum class ExtensionType {
None = 0,
Nsp = 1,
Nspd = 2,
};
bool HasSuffix(const char *str, const char *suffix) {
const size_t suffix_len = std::strlen(suffix);
const size_t str_len = std::strlen(str);
if (suffix_len > str_len) {
return false;
}
return (PathView(str).substr(str_len - suffix_len) == PathView(suffix));
}
class HostPackageReader {
NON_COPYABLE(HostPackageReader);
NON_MOVEABLE(HostPackageReader);
private:
char m_content_path[fs::EntryNameLengthMax] = {};
ExtensionType m_extension_type = ExtensionType::None;
char m_mount_name[fs::MountNameLengthMax] = {};
bool m_is_mounted = false;
ncm::AutoBuffer m_content_meta_buffer;
ncm::ProgramId m_program_id = ncm::InvalidProgramId;
u32 m_program_version = 0;
ncm::ContentMetaType m_content_meta_type = static_cast<ncm::ContentMetaType>(0);
u8 m_program_index = 0;
public:
HostPackageReader() : m_content_meta_buffer() { /* ... */ }
~HostPackageReader() {
if (m_is_mounted) {
fs::Unmount(m_mount_name);
}
}
Result Initialize(const char *package, const char *mount) {
/* Copy in the content path. */
R_UNLESS(strlen(package) <= sizeof(m_content_path) - 1, pgl::ResultBufferNotEnough());
std::strcpy(m_content_path, package);
/* Set the extension type. */
R_TRY(this->SetExtensionType());
/* Copy in mount name. */
R_UNLESS(strlen(mount) <= sizeof(m_mount_name) - 1, pgl::ResultBufferNotEnough());
std::strcpy(m_mount_name, mount);
/* Mount the package. */
R_TRY(fs::MountApplicationPackage(m_mount_name, m_content_path));
m_is_mounted = true;
/* Set the content meta buffer. */
R_TRY(this->SetContentMetaBuffer());
/* Ensure we have a content meta buffer. */
R_UNLESS(m_content_meta_buffer.Get() != nullptr, pgl::ResultContentMetaNotFound());
R_SUCCEED();
}
Result ReadProgramInfo() {
/* First, read the program index. */
R_TRY(this->GetProgramIndex(std::addressof(m_program_index)));
/* Next, create a key for the rest of the fields. */
const auto key = ncm::PackagedContentMetaReader(m_content_meta_buffer.Get(), m_content_meta_buffer.GetSize()).GetKey();
/* Set fields. */
m_program_id = {key.id};
m_program_version = key.version;
m_content_meta_type = key.type;
R_SUCCEED();
}
Result GetContentPath(lr::Path *out, ncm::ContentType type, util::optional<u8> index) const {
switch (m_extension_type) {
case ExtensionType::Nsp: R_RETURN(this->GetContentPathInNsp(out, type, index));
case ExtensionType::Nspd: R_RETURN(this->GetContentPathInNspd(out, type, index));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
ncm::ProgramId GetProgramId() const {
return m_program_id;
}
u32 GetProgramVersion() const {
return m_program_version;
}
ncm::ContentMetaType GetContentMetaType() const {
return m_content_meta_type;
}
u8 GetProgramIndex() const {
return m_program_index;
}
private:
Result GetContentPathInNsp(lr::Path *out, ncm::ContentType type, util::optional<u8> index) const {
/* Create a reader. */
auto reader = ncm::PackagedContentMetaReader(m_content_meta_buffer.Get(), m_content_meta_buffer.GetSize());
/* Get the content info. */
const ncm::PackagedContentInfo *content_info = nullptr;
if (index) {
content_info = reader.GetContentInfo(type, *index);
} else {
content_info = reader.GetContentInfo(type);
}
R_UNLESS(content_info != nullptr, pgl::ResultApplicationContentNotFound());
/* Get the content id string. */
ncm::ContentIdString id_str;
ncm::GetStringFromContentId(id_str.data, sizeof(id_str.data), content_info->GetId());
/* Get the file name. */
char file_name[ncm::ContentIdStringLength + 5];
const size_t len = util::SNPrintf(file_name, sizeof(file_name), "%s.nca", id_str.data);
R_UNLESS(len + 1 == sizeof(file_name), pgl::ResultBufferNotEnough());
/* Ensure we have the content. */
bool has_content;
R_TRY(this->SearchContent(std::addressof(has_content), out, file_name, fs::OpenDirectoryMode_File));
R_UNLESS(has_content, pgl::ResultApplicationContentNotFound());
R_SUCCEED();
}
Result GetContentPathInNspd(lr::Path *out, ncm::ContentType type, util::optional<u8> index) const {
AMS_UNUSED(index);
/* Get the content name. */
const char *content_name = nullptr;
switch (type) {
case ncm::ContentType::Program: content_name = "program"; break;
case ncm::ContentType::Control: content_name = "control"; break;
case ncm::ContentType::HtmlDocument: content_name = "htmlDocument"; break;
case ncm::ContentType::LegalInformation: content_name = "legalInformation"; break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Get the file name. */
/* NSPD does not support indexed content, so we always use 0 as the index. */
char file_name[0x20];
const size_t len = util::SNPrintf(file_name, sizeof(file_name), "%s%d.ncd", content_name, 0);
R_UNLESS(len + 1 <= sizeof(file_name), pgl::ResultBufferNotEnough());
/* Ensure we have the content. */
bool has_content;
R_TRY(this->SearchContent(std::addressof(has_content), out, file_name, fs::OpenDirectoryMode_Directory));
R_UNLESS(has_content, pgl::ResultApplicationContentNotFound());
R_SUCCEED();
}
Result GetProgramIndex(u8 *out) {
/* Nspd programs do not have indices. */
if (m_extension_type == ExtensionType::Nspd) {
*out = 0;
R_SUCCEED();
}
/* Create a reader. */
auto reader = ncm::PackagedContentMetaReader(m_content_meta_buffer.Get(), m_content_meta_buffer.GetSize());
/* Get the program content info. */
auto program_content_info = reader.GetContentInfo(ncm::ContentType::Program);
R_UNLESS(program_content_info, pgl::ResultApplicationContentNotFound());
/* Return the index. */
*out = program_content_info->GetIdOffset();
R_SUCCEED();
}
Result SetExtensionType() {
/* First, clear the suffix if the path is a program ncd. */
if (HasSuffix(m_content_path, "program0.ncd/")) {
m_content_path[strnlen(m_content_path, sizeof(m_content_path)) - std::strlen("program0.ncd/")] = 0;
}
if (HasSuffix(m_content_path, ".nsp")) {
m_extension_type = ExtensionType::Nsp;
R_SUCCEED();
} else if (HasSuffix(m_content_path, ".nspd") || HasSuffix(m_content_path, ".nspd/")) {
m_extension_type = ExtensionType::Nspd;
R_SUCCEED();
} else {
R_THROW(fs::ResultPathNotFound());
}
}
Result SetContentMetaBuffer() {
constexpr const char ContentMetaFileExtension[] = ".cnmt.nca";
constexpr const char ContentMetaDirectoryExtension[] = "meta0.ncd";
/* Find the Content meta path. */
bool has_content = false;
lr::Path meta_path;
switch (m_extension_type) {
case ExtensionType::Nsp: R_TRY(this->SearchContent(std::addressof(has_content), std::addressof(meta_path), ContentMetaFileExtension, fs::OpenDirectoryMode_File)); break;
case ExtensionType::Nspd: R_TRY(this->SearchContent(std::addressof(has_content), std::addressof(meta_path), ContentMetaDirectoryExtension, fs::OpenDirectoryMode_Directory)); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
R_UNLESS(has_content, pgl::ResultContentMetaNotFound());
/* Read the content meta buffer. */
R_RETURN(ncm::TryReadContentMetaPath(std::addressof(m_content_meta_buffer), meta_path.str, ncm::ReadContentMetaPathWithoutExtendedDataOrDigest));
}
Result SearchContent(bool *out, lr::Path *out_path, const char *extension, fs::OpenDirectoryMode mode) const {
/* Generate the root directory path. */
char root_dir[sizeof(m_mount_name) + 2];
util::SNPrintf(root_dir, sizeof(root_dir), "%s:/", m_mount_name);
/* Open the root directory. */
fs::DirectoryHandle dir;
R_TRY(fs::OpenDirectory(std::addressof(dir), root_dir, mode));
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
/* Iterate over directory entries. */
while (true) {
fs::DirectoryEntry entry;
s64 count;
R_TRY(fs::ReadDirectory(std::addressof(count), std::addressof(entry), dir, 1));
if (count == 0) {
break;
}
/* Check if we match the suffix. */
if (HasSuffix(entry.name, extension)) {
*out = true;
if (out_path) {
const size_t len = util::SNPrintf(out_path->str, sizeof(out_path->str), "%s/%s", m_content_path, entry.name);
R_UNLESS(len + 1 < sizeof(out_path->str), pgl::ResultBufferNotEnough());
if (entry.type == fs::DirectoryEntryType_Directory) {
out_path->str[len] = '/';
out_path->str[len + 1] = 0;
}
}
R_SUCCEED();
}
}
/* We didn't find a match. */
*out = false;
R_SUCCEED();
}
};
}
Result LaunchProgramFromHost(os::ProcessId *out, const char *package_path, u32 pm_flags) {
/* Read the package. */
HostPackageReader reader;
R_TRY(reader.Initialize(package_path, HostPackageMountName));
/* Read the program info. */
R_TRY(reader.ReadProgramInfo());
/* Open a host location resolver. */
lr::LocationResolver host_resolver;
R_TRY(lr::OpenLocationResolver(std::addressof(host_resolver), ncm::StorageId::Host));
/* Get the content path. */
lr::Path content_path;
R_TRY(reader.GetContentPath(std::addressof(content_path), ncm::ContentType::Program, reader.GetProgramIndex()));
/* Erase the program redirection. */
R_TRY(host_resolver.EraseProgramRedirection(reader.GetProgramId()));
/* Redirect the program path to point to the new path. */
host_resolver.RedirectProgramPath(content_path, reader.GetProgramId());
/* Launch the program. */
R_RETURN(pgl::srv::LaunchProgram(out, ncm::ProgramLocation::Make(reader.GetProgramId(), ncm::StorageId::Host), pm_flags, pgl::LaunchFlags_None));
}
Result GetHostContentMetaInfo(pgl::ContentMetaInfo *out, const char *package_path) {
/* Read the package. */
HostPackageReader reader;
R_TRY(reader.Initialize(package_path, HostPackageMountName));
/* Read the program info. */
R_TRY(reader.ReadProgramInfo());
/* Get the content meta info. */
*out = {
.id = reader.GetProgramId().value,
.version = reader.GetProgramVersion(),
.content_type = ncm::ContentType::Program,
.id_offset = reader.GetProgramIndex(),
};
R_SUCCEED();
}
}

View File

@@ -1,24 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::pgl::srv {
Result LaunchProgramFromHost(os::ProcessId *out, const char *content_path, u32 pm_flags);
Result GetHostContentMetaInfo(pgl::ContentMetaInfo *out, const char *content_path);
}

View File

@@ -1,177 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 "pgl_srv_shell.hpp"
#include "pgl_srv_shell_event_observer.hpp"
#include "pgl_srv_shell_host_utils.hpp"
#include "pgl_srv_tipc_utils.hpp"
namespace ams::pgl::srv {
Result ShellInterfaceCommon::LaunchProgramImpl(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags) {
R_RETURN(pgl::srv::LaunchProgram(out, loc, pm_flags, pgl_flags));
}
Result ShellInterfaceCommon::TerminateProcessImpl(os::ProcessId process_id) {
R_RETURN(pgl::srv::TerminateProcess(process_id));
}
Result ShellInterfaceCommon::LaunchProgramFromHostImpl(os::ProcessId *out, const void *content_path, size_t content_path_size, u32 pm_flags) {
AMS_UNUSED(content_path_size);
R_RETURN(pgl::srv::LaunchProgramFromHost(out, static_cast<const char *>(content_path), pm_flags));
}
Result ShellInterfaceCommon::GetHostContentMetaInfoImpl(pgl::ContentMetaInfo *out, const void *content_path, size_t content_path_size) {
AMS_UNUSED(content_path_size);
R_RETURN(pgl::srv::GetHostContentMetaInfo(out, static_cast<const char *>(content_path)));
}
Result ShellInterfaceCommon::GetApplicationProcessIdImpl(os::ProcessId *out) {
R_RETURN(pgl::srv::GetApplicationProcessId(out));
}
Result ShellInterfaceCommon::BoostSystemMemoryResourceLimitImpl(u64 size) {
R_RETURN(pgl::srv::BoostSystemMemoryResourceLimit(size));
}
Result ShellInterfaceCommon::IsProcessTrackedImpl(bool *out, os::ProcessId process_id) {
*out = pgl::srv::IsProcessTracked(process_id);
R_SUCCEED();
}
Result ShellInterfaceCommon::EnableApplicationCrashReportImpl(bool enabled) {
pgl::srv::EnableApplicationCrashReport(enabled);
R_SUCCEED();
}
Result ShellInterfaceCommon::IsApplicationCrashReportEnabledImpl(bool *out) {
*out = pgl::srv::IsApplicationCrashReportEnabled();
R_SUCCEED();
}
Result ShellInterfaceCommon::EnableApplicationAllThreadDumpOnCrashImpl(bool enabled) {
pgl::srv::EnableApplicationAllThreadDumpOnCrash(enabled);
R_SUCCEED();
}
Result ShellInterfaceCommon::TriggerApplicationSnapShotDumperImpl(SnapShotDumpType dump_type, const void *arg, size_t arg_size) {
AMS_UNUSED(arg_size);
R_RETURN(pgl::srv::TriggerApplicationSnapShotDumper(dump_type, static_cast<const char *>(arg)));
}
Result ShellInterfaceCmif::LaunchProgram(ams::sf::Out<os::ProcessId> out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags) {
R_RETURN(this->LaunchProgramImpl(out.GetPointer(), loc, pm_flags, pgl_flags));
}
Result ShellInterfaceCmif::TerminateProcess(os::ProcessId process_id) {
R_RETURN(this->TerminateProcessImpl(process_id));
}
Result ShellInterfaceCmif::LaunchProgramFromHost(ams::sf::Out<os::ProcessId> out, const ams::sf::InBuffer &content_path, u32 pm_flags) {
R_RETURN(this->LaunchProgramFromHostImpl(out.GetPointer(), content_path.GetPointer(), content_path.GetSize(), pm_flags));
}
Result ShellInterfaceCmif::GetHostContentMetaInfo(ams::sf::Out<pgl::ContentMetaInfo> out, const ams::sf::InBuffer &content_path) {
R_RETURN(this->GetHostContentMetaInfoImpl(out.GetPointer(), content_path.GetPointer(), content_path.GetSize()));
}
Result ShellInterfaceCmif::GetApplicationProcessId(ams::sf::Out<os::ProcessId> out) {
R_RETURN(this->GetApplicationProcessIdImpl(out.GetPointer()));
}
Result ShellInterfaceCmif::BoostSystemMemoryResourceLimit(u64 size) {
R_RETURN(this->BoostSystemMemoryResourceLimitImpl(size));
}
Result ShellInterfaceCmif::IsProcessTracked(ams::sf::Out<bool> out, os::ProcessId process_id) {
R_RETURN(this->IsProcessTrackedImpl(out.GetPointer(), process_id));
}
Result ShellInterfaceCmif::EnableApplicationCrashReport(bool enabled) {
R_RETURN(this->EnableApplicationCrashReportImpl(enabled));
}
Result ShellInterfaceCmif::IsApplicationCrashReportEnabled(ams::sf::Out<bool> out) {
R_RETURN(this->IsApplicationCrashReportEnabledImpl(out.GetPointer()));
}
Result ShellInterfaceCmif::EnableApplicationAllThreadDumpOnCrash(bool enabled) {
R_RETURN(this->EnableApplicationAllThreadDumpOnCrashImpl(enabled));
}
Result ShellInterfaceCmif::TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const ams::sf::InBuffer &arg) {
R_RETURN(this->TriggerApplicationSnapShotDumperImpl(dump_type, arg.GetPointer(), arg.GetSize()));
}
Result ShellInterfaceCmif::GetShellEventObserver(ams::sf::Out<ams::sf::SharedPointer<pgl::sf::IEventObserver>> out) {
/* Allocate a new interface. */
auto session = ObjectFactory::CreateSharedEmplaced<pgl::sf::IEventObserver, ShellEventObserverCmif>(m_allocator);
R_UNLESS(session != nullptr, pgl::ResultOutOfMemory());
*out = std::move(session);
R_SUCCEED();
}
Result ShellInterfaceCmif::Command21NotImplemented(ams::sf::Out<u64> out, u32 in, const ams::sf::InBuffer &buf1, const ams::sf::InBuffer &buf2) {
AMS_UNUSED(out, in, buf1, buf2);
R_THROW(pgl::ResultNotImplemented());
}
Result ShellInterfaceTipc::LaunchProgram(ams::tipc::Out<os::ProcessId> out, const ncm::ProgramLocation loc, u32 pm_flags, u8 pgl_flags) {
R_RETURN(this->LaunchProgramImpl(out.GetPointer(), loc, pm_flags, pgl_flags));
}
Result ShellInterfaceTipc::TerminateProcess(os::ProcessId process_id) {
R_RETURN(this->TerminateProcessImpl(process_id));
}
Result ShellInterfaceTipc::LaunchProgramFromHost(ams::tipc::Out<os::ProcessId> out, const ams::tipc::InBuffer content_path, u32 pm_flags) {
R_RETURN(this->LaunchProgramFromHostImpl(out.GetPointer(), content_path.GetPointer(), content_path.GetSize(), pm_flags));
}
Result ShellInterfaceTipc::GetHostContentMetaInfo(ams::tipc::Out<pgl::ContentMetaInfo> out, const ams::tipc::InBuffer content_path) {
R_RETURN(this->GetHostContentMetaInfoImpl(out.GetPointer(), content_path.GetPointer(), content_path.GetSize()));
}
Result ShellInterfaceTipc::GetApplicationProcessId(ams::tipc::Out<os::ProcessId> out) {
R_RETURN(this->GetApplicationProcessIdImpl(out.GetPointer()));
}
Result ShellInterfaceTipc::BoostSystemMemoryResourceLimit(u64 size) {
R_RETURN(this->BoostSystemMemoryResourceLimitImpl(size));
}
Result ShellInterfaceTipc::IsProcessTracked(ams::tipc::Out<bool> out, os::ProcessId process_id) {
R_RETURN(this->IsProcessTrackedImpl(out.GetPointer(), process_id));
}
Result ShellInterfaceTipc::EnableApplicationCrashReport(bool enabled) {
R_RETURN(this->EnableApplicationCrashReportImpl(enabled));
}
Result ShellInterfaceTipc::IsApplicationCrashReportEnabled(ams::tipc::Out<bool> out) {
R_RETURN(this->IsApplicationCrashReportEnabledImpl(out.GetPointer()));
}
Result ShellInterfaceTipc::EnableApplicationAllThreadDumpOnCrash(bool enabled) {
R_RETURN(this->EnableApplicationAllThreadDumpOnCrashImpl(enabled));
}
Result ShellInterfaceTipc::GetShellEventObserver(ams::tipc::OutMoveHandle out) {
R_RETURN(pgl::srv::AllocateShellEventObserverForTipc(out.GetPointer()));
}
}

View File

@@ -1,23 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::pgl::srv {
Result AllocateShellEventObserverForTipc(os::NativeHandle *out);
}