Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -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
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user