From 98cc051387515977dcdede8ab0f1b09077f2f24b Mon Sep 17 00:00:00 2001 From: SciresM Date: Thu, 16 Apr 2020 19:55:47 -0700 Subject: [PATCH] pgl: Reimplement the pgl sysmodule (#896) * pgl: add skeleton folder to stratosphere * pgl: Add service interface for IShellInterface * pgl: begin skeletoning shell service, implement two commands. * pgl: Implement three more commands. * pgl: implement bool tracking commands * pgl: Implement TriggerApplicationSnapShotDumper * pgl: implement InitializeProcessControlTask * pgl: Implement pgl::srv::Initialize * pgl: Implement main() * pgl: Implement (Get)ShellEventObserver * pgl: implement LaunchProgramFromHost, GetHostContentMetaInfo * pgl: Implement ProcessControlTask * settings: fix duplicate object name * pgl: fix minor bugs in impl --- .../include/stratosphere/ldr.hpp | 5 +- .../stratosphere/ldr/ldr_shell_api.hpp | 31 ++ .../ncm/ncm_system_content_meta_id.hpp | 28 + .../include/stratosphere/os.hpp | 1 + .../include/stratosphere/os/os_sdk_mutex.hpp | 60 +++ .../include/stratosphere/pgl.hpp | 2 + .../stratosphere/pgl/pgl_shell_api.hpp | 2 +- .../include/stratosphere/pgl/pgl_types.hpp | 1 + .../pgl/sf/pgl_sf_i_shell_interface.hpp | 74 +++ .../stratosphere/pgl/srv/pgl_srv_api.hpp | 25 + .../pgl/srv/pgl_srv_shell_interface.hpp | 52 ++ .../include/stratosphere/pm/pm_shell_api.hpp | 8 +- .../include/stratosphere/pm/pm_types.hpp | 26 +- .../include/stratosphere/settings.hpp | 1 + .../settings/system/settings_error_report.hpp | 31 ++ .../include/stratosphere/sf.hpp | 19 +- .../stratosphere/sf/sf_lmem_utility.hpp | 67 +++ .../source/diag/diag_assertion_impl.cpp | 2 +- .../fssystem/fssystem_pooled_buffer.cpp | 2 +- .../source/ldr/ldr_shell_api.cpp | 37 ++ .../lr/lr_remote_location_resolver_impl.hpp | 25 +- .../source/os/os_sdk_mutex.cpp | 43 ++ .../source/pgl/pgl_shell_api.cpp | 4 +- .../source/pgl/srv/pgl_srv_api.cpp | 38 ++ .../source/pgl/srv/pgl_srv_shell.cpp | 485 ++++++++++++++++++ .../source/pgl/srv/pgl_srv_shell.hpp | 38 ++ .../pgl/srv/pgl_srv_shell_event_observer.cpp | 78 +++ .../pgl/srv/pgl_srv_shell_event_observer.hpp | 78 +++ .../pgl/srv/pgl_srv_shell_host_utils.cpp | 355 +++++++++++++ .../pgl/srv/pgl_srv_shell_host_utils.hpp | 24 + .../pgl/srv/pgl_srv_shell_interface.cpp | 90 ++++ .../source/pm/pm_shell_api.cpp | 33 +- .../impl/settings_error_report_impl.cpp | 26 + .../impl/settings_error_report_impl.hpp | 23 + .../source/settings/settings_error_report.cpp | 27 + .../source/settings/settings_fwdbg_api.cpp | 7 +- .../libvapours/include/vapours/results.hpp | 1 + .../include/vapours/results/pgl_results.hpp | 30 ++ stratosphere/pgl/Makefile | 128 +++++ stratosphere/pgl/pgl.json | 88 ++++ stratosphere/pgl/source/pgl_main.cpp | 160 ++++++ .../pm/source/impl/pm_process_manager.cpp | 20 - 42 files changed, 2218 insertions(+), 57 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/ldr/ldr_shell_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/os/os_sdk_mutex.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/pgl/sf/pgl_sf_i_shell_interface.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_shell_interface.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/settings/system/settings_error_report.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/sf/sf_lmem_utility.hpp create mode 100644 libraries/libstratosphere/source/ldr/ldr_shell_api.cpp create mode 100644 libraries/libstratosphere/source/os/os_sdk_mutex.cpp create mode 100644 libraries/libstratosphere/source/pgl/srv/pgl_srv_api.cpp create mode 100644 libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp create mode 100644 libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.hpp create mode 100644 libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.cpp create mode 100644 libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.hpp create mode 100644 libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_host_utils.cpp create mode 100644 libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_host_utils.hpp create mode 100644 libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_interface.cpp create mode 100644 libraries/libstratosphere/source/settings/impl/settings_error_report_impl.cpp create mode 100644 libraries/libstratosphere/source/settings/impl/settings_error_report_impl.hpp create mode 100644 libraries/libstratosphere/source/settings/settings_error_report.cpp create mode 100644 libraries/libvapours/include/vapours/results/pgl_results.hpp create mode 100644 stratosphere/pgl/Makefile create mode 100644 stratosphere/pgl/pgl.json create mode 100644 stratosphere/pgl/source/pgl_main.cpp diff --git a/libraries/libstratosphere/include/stratosphere/ldr.hpp b/libraries/libstratosphere/include/stratosphere/ldr.hpp index c3474f63e..c278de699 100644 --- a/libraries/libstratosphere/include/stratosphere/ldr.hpp +++ b/libraries/libstratosphere/include/stratosphere/ldr.hpp @@ -16,5 +16,6 @@ #pragma once -#include "ldr/ldr_types.hpp" -#include "ldr/ldr_pm_api.hpp" +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/ldr/ldr_shell_api.hpp b/libraries/libstratosphere/include/stratosphere/ldr/ldr_shell_api.hpp new file mode 100644 index 000000000..24a33e9c9 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ldr/ldr_shell_api.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include + +namespace ams::ldr { + + /* Shell API. */ + Result InitializeForShell(); + Result FinalizeForShell(); + + Result SetProgramArgument(ncm::ProgramId program_id, const void *arg, size_t size); + Result FlushArguments(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp index 42d2531a0..7154243cb 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp @@ -402,6 +402,34 @@ namespace ams::ncm { return true; } + struct SystemDebugAppletId { + u64 value; + + constexpr operator ProgramId() const { + return { this->value }; + } + + static const SystemDebugAppletId Start; + + static const SystemDebugAppletId SnapShotDumper; + + static const SystemDebugAppletId End; + }; + + inline constexpr const SystemDebugAppletId SystemDebugAppletId::Start = { 0x0100000000002000ul }; + + inline constexpr const SystemDebugAppletId SystemDebugAppletId::SnapShotDumper = { 0x0100000000002071ul }; + + inline constexpr const SystemDebugAppletId SystemDebugAppletId::End = { 0x0100000000002FFFul }; + + inline constexpr bool IsSystemDebugAppletId(const ProgramId &program_id) { + return SystemDebugAppletId::Start <= program_id && program_id <= SystemDebugAppletId::End; + } + + inline constexpr bool IsSystemDebugAppletId(const SystemDebugAppletId &program_id) { + return true; + } + struct LibraryAppletId { u64 value; diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp index 57578afda..e67cd32a6 100644 --- a/libraries/libstratosphere/include/stratosphere/os.hpp +++ b/libraries/libstratosphere/include/stratosphere/os.hpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/os/os_sdk_mutex.hpp b/libraries/libstratosphere/include/stratosphere/os/os_sdk_mutex.hpp new file mode 100644 index 000000000..675c12ac2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_sdk_mutex.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::os { + + class SdkConditionVariable; + + struct SdkMutexType { + union { + s32 _arr[sizeof(impl::InternalCriticalSectionStorage) / sizeof(s32)]; + impl::InternalCriticalSectionStorage _storage; + }; + }; + static_assert(std::is_trivial::value); + + void InitializeSdkMutex(SdkMutexType *mutex); + + void LockSdkMutex(SdkMutexType *mutex); + bool TryLockSdkMutex(SdkMutexType *mutex); + void UnlockSdkMutex(SdkMutexType *mutex); + + bool IsSdkMutexLockedByCurrentThread(const SdkMutexType *mutex); + + class SdkMutex { + private: + friend class SdkConditionVariable; + private: + SdkMutexType mutex; + public: + constexpr SdkMutex() : mutex{{0}} { /* ... */ } + + ALWAYS_INLINE void Lock() { return os::LockSdkMutex(std::addressof(this->mutex)); } + ALWAYS_INLINE bool TryLock() { return os::TryLockSdkMutex(std::addressof(this->mutex)); } + ALWAYS_INLINE void Unlock() { return os::UnlockSdkMutex(std::addressof(this->mutex)); } + + ALWAYS_INLINE bool IsLockedByCurrentThread() const { return os::IsSdkMutexLockedByCurrentThread(std::addressof(this->mutex)); } + + ALWAYS_INLINE void lock() { return this->Lock(); } + ALWAYS_INLINE bool try_lock() { return this->TryLock(); } + ALWAYS_INLINE void unlock() { return this->Unlock(); } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/pgl.hpp b/libraries/libstratosphere/include/stratosphere/pgl.hpp index 3d2ffeb81..556e1e5f2 100644 --- a/libraries/libstratosphere/include/stratosphere/pgl.hpp +++ b/libraries/libstratosphere/include/stratosphere/pgl.hpp @@ -19,3 +19,5 @@ #include #include #include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/pgl/pgl_shell_api.hpp b/libraries/libstratosphere/include/stratosphere/pgl/pgl_shell_api.hpp index 7dbe28d08..f14621ec3 100644 --- a/libraries/libstratosphere/include/stratosphere/pgl/pgl_shell_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/pgl/pgl_shell_api.hpp @@ -34,7 +34,7 @@ namespace ams::pgl { Result EnableApplicationCrashReport(bool enabled); Result IsApplicationCrashReportEnabled(bool *out); Result EnableApplicationAllThreadDumpOnCrash(bool enabled); - Result TriggerSnapShotDumper(const char *arg, SnapShotDumpType dump_type); + Result TriggerApplicationSnapShotDumper(const char *arg, SnapShotDumpType dump_type); Result GetEventObserver(pgl::EventObserver *out); diff --git a/libraries/libstratosphere/include/stratosphere/pgl/pgl_types.hpp b/libraries/libstratosphere/include/stratosphere/pgl/pgl_types.hpp index e2fa9ce42..08aceb8f8 100644 --- a/libraries/libstratosphere/include/stratosphere/pgl/pgl_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/pgl/pgl_types.hpp @@ -23,6 +23,7 @@ namespace ams::pgl { enum LaunchFlags : u8 { + LaunchFlags_None = 0, LaunchFlags_EnableDetailedCrashReport = (1 << 0), LaunchFlags_EnableCrashReportScreenShotForProduction = (1 << 1), LaunchFlags_EnableCrashReportScreenShotForDevelop = (1 << 2), diff --git a/libraries/libstratosphere/include/stratosphere/pgl/sf/pgl_sf_i_shell_interface.hpp b/libraries/libstratosphere/include/stratosphere/pgl/sf/pgl_sf_i_shell_interface.hpp new file mode 100644 index 000000000..c44d72924 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pgl/sf/pgl_sf_i_shell_interface.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include +#include + +namespace ams::pgl::sf { + + class IShellInterface : public ams::sf::IServiceObject { + protected: + enum class CommandId { + LaunchProgram = 0, + TerminateProcess = 1, + LaunchProgramFromHost = 2, + GetHostContentMetaInfo = 4, + GetApplicationProcessId = 5, + BoostSystemMemoryResourceLimit = 6, + IsProcessTracked = 7, + EnableApplicationCrashReport = 8, + IsApplicationCrashReportEnabled = 9, + EnableApplicationAllThreadDumpOnCrash = 10, + TriggerApplicationSnapShotDumper = 12, + GetShellEventObserver = 20, + }; + public: + /* Actual commands. */ + virtual Result LaunchProgram(ams::sf::Out out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags) = 0; + virtual Result TerminateProcess(os::ProcessId process_id) = 0; + virtual Result LaunchProgramFromHost(ams::sf::Out out, const ams::sf::InBuffer &content_path, u32 pm_flags) = 0; + virtual Result GetHostContentMetaInfo(ams::sf::Out out, const ams::sf::InBuffer &content_path) = 0; + virtual Result GetApplicationProcessId(ams::sf::Out out) = 0; + virtual Result BoostSystemMemoryResourceLimit(u64 size) = 0; + virtual Result IsProcessTracked(ams::sf::Out out, os::ProcessId process_id) = 0; + virtual Result EnableApplicationCrashReport(bool enabled) = 0; + virtual Result IsApplicationCrashReportEnabled(ams::sf::Out out) = 0; + virtual Result EnableApplicationAllThreadDumpOnCrash(bool enabled) = 0; + virtual Result TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const ams::sf::InBuffer &arg) = 0; + + virtual Result GetShellEventObserver(ams::sf::Out> out) = 0; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(LaunchProgram), + MAKE_SERVICE_COMMAND_META(TerminateProcess), + MAKE_SERVICE_COMMAND_META(LaunchProgramFromHost), + MAKE_SERVICE_COMMAND_META(GetHostContentMetaInfo), + MAKE_SERVICE_COMMAND_META(GetApplicationProcessId), + MAKE_SERVICE_COMMAND_META(BoostSystemMemoryResourceLimit), + MAKE_SERVICE_COMMAND_META(IsProcessTracked), + MAKE_SERVICE_COMMAND_META(EnableApplicationCrashReport), + MAKE_SERVICE_COMMAND_META(IsApplicationCrashReportEnabled), + MAKE_SERVICE_COMMAND_META(EnableApplicationAllThreadDumpOnCrash), + MAKE_SERVICE_COMMAND_META(TriggerApplicationSnapShotDumper), + MAKE_SERVICE_COMMAND_META(GetShellEventObserver), + }; + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_api.hpp b/libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_api.hpp new file mode 100644 index 000000000..ab49b14af --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_api.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::pgl::srv { + + void Initialize(ShellInterface *interface, MemoryResource *mr); + +} diff --git a/libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_shell_interface.hpp b/libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_shell_interface.hpp new file mode 100644 index 000000000..40f74f3ba --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pgl/srv/pgl_srv_shell_interface.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::pgl::srv { + + class ShellInterface final : public pgl::sf::IShellInterface { + NON_COPYABLE(ShellInterface); + NON_MOVEABLE(ShellInterface); + private: + MemoryResource *memory_resource; + public: + constexpr ShellInterface() : memory_resource(nullptr) { /* ... */ } + + void Initialize(MemoryResource *mr) { + AMS_ASSERT(this->memory_resource == nullptr); + this->memory_resource = mr; + } + public: + /* Interface commands. */ + virtual Result LaunchProgram(ams::sf::Out out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags) override final; + virtual Result TerminateProcess(os::ProcessId process_id) override final; + virtual Result LaunchProgramFromHost(ams::sf::Out out, const ams::sf::InBuffer &content_path, u32 pm_flags) override final; + virtual Result GetHostContentMetaInfo(ams::sf::Out out, const ams::sf::InBuffer &content_path) override final; + virtual Result GetApplicationProcessId(ams::sf::Out out) override final; + virtual Result BoostSystemMemoryResourceLimit(u64 size) override final; + virtual Result IsProcessTracked(ams::sf::Out out, os::ProcessId process_id) override final; + virtual Result EnableApplicationCrashReport(bool enabled) override final; + virtual Result IsApplicationCrashReportEnabled(ams::sf::Out out) override final; + virtual Result EnableApplicationAllThreadDumpOnCrash(bool enabled) override final; + virtual Result TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const ams::sf::InBuffer &arg) override final; + + virtual Result GetShellEventObserver(ams::sf::Out> out) override final; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/pm/pm_shell_api.hpp b/libraries/libstratosphere/include/stratosphere/pm/pm_shell_api.hpp index 9464b01b5..c38452710 100644 --- a/libraries/libstratosphere/include/stratosphere/pm/pm_shell_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/pm/pm_shell_api.hpp @@ -23,6 +23,12 @@ namespace ams::pm::shell { /* Shell API. */ - Result LaunchProgram(os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 launch_flags); + Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 launch_flags); + Result TerminateProcess(os::ProcessId process_id); + Result GetProcessEventEvent(os::SystemEvent *out); + Result GetProcessEventInfo(ProcessEventInfo *out); + Result GetApplicationProcessIdForShell(os::ProcessId *out); + Result BoostSystemMemoryResourceLimit(u64 size); + Result EnableApplicationExtraThread(); } diff --git a/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp b/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp index 70bf622b3..f433bddb4 100644 --- a/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp @@ -32,9 +32,29 @@ namespace ams::pm { ResourceLimitGroup_Count, }; - using LimitableResource = ::LimitableResource; + enum LaunchFlags : u32 { + LaunchFlags_None = 0, + LaunchFlags_SignalOnExit = (1 << 0), + LaunchFlags_SignalOnStart = (1 << 1), + LaunchFlags_SignalOnException = (1 << 2), + LaunchFlags_SignalOnDebugEvent = (1 << 3), + LaunchFlags_StartSuspended = (1 << 4), + LaunchFlags_DisableAslr = (1 << 5), + }; - enum class ProcessEvent { + enum LaunchFlagsDeprecated : u32 { + LaunchFlagsDeprecated_None = 0, + LaunchFlagsDeprecated_SignalOnExit = (1 << 0), + LaunchFlagsDeprecated_StartSuspended = (1 << 1), + LaunchFlagsDeprecated_SignalOnException = (1 << 2), + LaunchFlagsDeprecated_DisableAslr = (1 << 3), + LaunchFlagsDeprecated_SignalOnDebugEvent = (1 << 4), + LaunchFlagsDeprecated_SignalOnStart = (1 << 5), + }; + + constexpr inline u32 LaunchFlagsMask = (1 << 6) - 1; + + enum class ProcessEvent : u32 { None = 0, Exited = 1, Started = 2, @@ -43,7 +63,7 @@ namespace ams::pm { DebugBreak = 5, }; - enum class ProcessEventDeprecated { + enum class ProcessEventDeprecated : u32 { None = 0, Exception = 1, Exited = 2, diff --git a/libraries/libstratosphere/include/stratosphere/settings.hpp b/libraries/libstratosphere/include/stratosphere/settings.hpp index 8c95dc127..a509fe93a 100644 --- a/libraries/libstratosphere/include/stratosphere/settings.hpp +++ b/libraries/libstratosphere/include/stratosphere/settings.hpp @@ -19,6 +19,7 @@ #include "settings/settings_types.hpp" #include "settings/settings_fwdbg_types.hpp" #include "settings/settings_fwdbg_api.hpp" +#include "settings/system/settings_error_report.hpp" #include "settings/system/settings_firmware_version.hpp" #include "settings/system/settings_product_model.hpp" #include "settings/system/settings_region.hpp" diff --git a/libraries/libstratosphere/include/stratosphere/settings/system/settings_error_report.hpp b/libraries/libstratosphere/include/stratosphere/settings/system/settings_error_report.hpp new file mode 100644 index 000000000..c0c67d5b8 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/settings/system/settings_error_report.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::settings::system { + + enum ErrorReportSharePermission { + ErrorReportSharePermission_NotConfirmed = 0, + ErrorReportSharePermission_Granted = 1, + ErrorReportSharePermission_Denied = 2, + }; + + ErrorReportSharePermission GetErrorReportSharePermission(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/sf.hpp b/libraries/libstratosphere/include/stratosphere/sf.hpp index be8aa5b27..b7bbe4737 100644 --- a/libraries/libstratosphere/include/stratosphere/sf.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf.hpp @@ -16,15 +16,16 @@ #pragma once -#include "sf/sf_common.hpp" -#include "sf/sf_mem_utility.hpp" -#include "sf/sf_service_object.hpp" -#include "sf/hipc/sf_hipc_server_session_manager.hpp" +#include +#include +#include +#include +#include -#include "sf/sf_out.hpp" -#include "sf/sf_buffers.hpp" -#include "sf/impl/sf_impl_command_serialization.hpp" +#include +#include +#include -#include "sf/hipc/sf_hipc_server_manager.hpp" +#include -#include "sf/sf_mitm_dispatch.h" +#include diff --git a/libraries/libstratosphere/include/stratosphere/sf/sf_lmem_utility.hpp b/libraries/libstratosphere/include/stratosphere/sf/sf_lmem_utility.hpp new file mode 100644 index 000000000..cb06140a6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/sf/sf_lmem_utility.hpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::sf { + + class ExpHeapMemoryResource : public MemoryResource { + private: + lmem::HeapHandle handle; + public: + explicit ExpHeapMemoryResource(lmem::HeapHandle h) : handle(h) { /* ... */ } + + lmem::HeapHandle GetHandle() const { return this->handle; } + private: + virtual void *AllocateImpl(size_t size, size_t alignment) override { + return lmem::AllocateFromExpHeap(this->handle, size, static_cast(alignment)); + } + + virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) override { + return lmem::FreeToExpHeap(this->handle, buffer); + } + + virtual bool IsEqualImpl(const MemoryResource &resource) const { + return this == std::addressof(resource); + } + }; + + class UnitHeapMemoryResource : public MemoryResource { + private: + lmem::HeapHandle handle; + public: + explicit UnitHeapMemoryResource(lmem::HeapHandle h) : handle(h) { /* ... */ } + + lmem::HeapHandle GetHandle() const { return this->handle; } + private: + virtual void *AllocateImpl(size_t size, size_t alignment) override { + AMS_ASSERT(size <= lmem::GetUnitHeapUnitSize(this->handle)); + AMS_ASSERT(alignment <= static_cast(lmem::GetUnitHeapAlignment(this->handle))); + return lmem::AllocateFromUnitHeap(this->handle); + } + + virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) override { + return lmem::FreeToUnitHeap(this->handle, buffer); + } + + virtual bool IsEqualImpl(const MemoryResource &resource) const { + return this == std::addressof(resource); + } + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp b/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp index ca66e9a1a..905d479a3 100644 --- a/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp +++ b/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp @@ -44,7 +44,7 @@ namespace ams::diag { inline void DebugLog(const char *format, ...) __attribute__((format(printf, 1, 2))); #ifdef AMS_ENABLE_DEBUG_PRINT - os::Mutex g_debug_log_lock; + os::Mutex g_debug_log_lock(true); char g_debug_buffer[0x400]; void DebugLogImpl(const char *format, ::std::va_list vl) { diff --git a/libraries/libstratosphere/source/fssystem/fssystem_pooled_buffer.cpp b/libraries/libstratosphere/source/fssystem/fssystem_pooled_buffer.cpp index 315d24e56..df32ec50a 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_pooled_buffer.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_pooled_buffer.cpp @@ -169,7 +169,7 @@ namespace ams::fssystem { if (this->size > ideal_size) { /* If we do, we need to have a buffer allocated from the heap. */ AMS_ASSERT(this->buffer != nullptr); - AMS_ASSERT(g_heap.GetBlockSize(), HeapBlockSize); + AMS_ASSERT(g_heap.GetBlockSize() == HeapBlockSize); const size_t new_size = util::AlignUp(ideal_size, HeapBlockSize); diff --git a/libraries/libstratosphere/source/ldr/ldr_shell_api.cpp b/libraries/libstratosphere/source/ldr/ldr_shell_api.cpp new file mode 100644 index 000000000..b5391a14b --- /dev/null +++ b/libraries/libstratosphere/source/ldr/ldr_shell_api.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ldr { + + Result InitializeForShell() { + return ::ldrShellInitialize(); + } + + Result FinalizeForShell() { + ::ldrShellExit(); + return ResultSuccess(); + } + + Result SetProgramArgument(ncm::ProgramId program_id, const void *arg, size_t size) { + return ::ldrShellSetProgramArguments(static_cast(program_id), arg, size); + } + + Result FlushArguments() { + return ::ldrShellFlushArguments(); + } + +} diff --git a/libraries/libstratosphere/source/lr/lr_remote_location_resolver_impl.hpp b/libraries/libstratosphere/source/lr/lr_remote_location_resolver_impl.hpp index 5ac2b47e4..59093e7d8 100644 --- a/libraries/libstratosphere/source/lr/lr_remote_location_resolver_impl.hpp +++ b/libraries/libstratosphere/source/lr/lr_remote_location_resolver_impl.hpp @@ -28,19 +28,19 @@ namespace ams::lr { public: /* Actual commands. */ virtual Result ResolveProgramPath(sf::Out out, ncm::ProgramId id) override { - return lrLrResolveProgramPath(std::addressof(this->srv), static_cast(id), out->str); + return lrLrResolveProgramPath(std::addressof(this->srv), id.value, out->str); } virtual Result RedirectProgramPath(const Path &path, ncm::ProgramId id) override { - return lrLrRedirectProgramPath(std::addressof(this->srv), static_cast(id), path.str); + return lrLrRedirectProgramPath(std::addressof(this->srv), id.value, path.str); } virtual Result ResolveApplicationControlPath(sf::Out out, ncm::ProgramId id) override { - return lrLrResolveApplicationControlPath(std::addressof(this->srv), static_cast(id), out->str); + return lrLrResolveApplicationControlPath(std::addressof(this->srv), id.value, out->str); } virtual Result ResolveApplicationHtmlDocumentPath(sf::Out out, ncm::ProgramId id) override { - return lrLrResolveApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast(id), out->str); + return lrLrResolveApplicationHtmlDocumentPath(std::addressof(this->srv), id.value, out->str); } virtual Result ResolveDataPath(sf::Out out, ncm::DataId id) override { @@ -48,31 +48,31 @@ namespace ams::lr { } virtual Result RedirectApplicationControlPathDeprecated(const Path &path, ncm::ProgramId id) override { - return lrLrRedirectApplicationControlPath(std::addressof(this->srv), static_cast(id), 0, path.str); + return lrLrRedirectApplicationControlPath(std::addressof(this->srv), id.value, 0, path.str); } virtual Result RedirectApplicationControlPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override { - return lrLrRedirectApplicationControlPath(std::addressof(this->srv), static_cast(id), static_cast(owner_id), path.str); + return lrLrRedirectApplicationControlPath(std::addressof(this->srv), id.value, owner_id.value, path.str); } virtual Result RedirectApplicationHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) override { - return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast(id), 0, path.str); + return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), id.value, 0, path.str); } virtual Result RedirectApplicationHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override { - return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast(id), static_cast(owner_id), path.str); + return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), id.value, owner_id.value, path.str); } virtual Result ResolveApplicationLegalInformationPath(sf::Out out, ncm::ProgramId id) override { - return lrLrResolveApplicationLegalInformationPath(std::addressof(this->srv), static_cast(id), out->str); + return lrLrResolveApplicationLegalInformationPath(std::addressof(this->srv), id.value, out->str); } virtual Result RedirectApplicationLegalInformationPathDeprecated(const Path &path, ncm::ProgramId id) override { - return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), static_cast(id), 0, path.str); + return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), id.value, 0, path.str); } virtual Result RedirectApplicationLegalInformationPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override { - return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), static_cast(id), static_cast(owner_id), path.str); + return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), id.value, owner_id.value, path.str); } virtual Result Refresh() override { @@ -100,8 +100,7 @@ namespace ams::lr { } virtual Result EraseProgramRedirection(ncm::ProgramId id) override { - /* TODO: libnx bindings */ - AMS_ABORT(); + return lrLrEraseProgramRedirection(std::addressof(this->srv), id.value); } virtual Result EraseApplicationControlRedirection(ncm::ProgramId id) override { diff --git a/libraries/libstratosphere/source/os/os_sdk_mutex.cpp b/libraries/libstratosphere/source/os/os_sdk_mutex.cpp new file mode 100644 index 000000000..e7dbeb56a --- /dev/null +++ b/libraries/libstratosphere/source/os/os_sdk_mutex.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::os { + + void InitializeSdkMutex(SdkMutexType *mutex) { + GetReference(mutex->_storage).Initialize(); + } + + bool IsSdkMutexLockedByCurrentThread(const SdkMutexType *mutex) { + return GetReference(mutex->_storage).IsLockedByCurrentThread(); + } + + void LockSdkMutex(SdkMutexType *mutex) { + AMS_ABORT_UNLESS(!IsSdkMutexLockedByCurrentThread(mutex)); + return GetReference(mutex->_storage).Enter(); + } + + bool TryLockSdkMutex(SdkMutexType *mutex) { + AMS_ABORT_UNLESS(!IsSdkMutexLockedByCurrentThread(mutex)); + return GetReference(mutex->_storage).TryEnter(); + } + + void UnlockSdkMutex(SdkMutexType *mutex) { + AMS_ABORT_UNLESS(IsSdkMutexLockedByCurrentThread(mutex)); + return GetReference(mutex->_storage).Leave(); + } + +} diff --git a/libraries/libstratosphere/source/pgl/pgl_shell_api.cpp b/libraries/libstratosphere/source/pgl/pgl_shell_api.cpp index cbed6bbb3..66c2bc2d0 100644 --- a/libraries/libstratosphere/source/pgl/pgl_shell_api.cpp +++ b/libraries/libstratosphere/source/pgl/pgl_shell_api.cpp @@ -71,8 +71,8 @@ namespace ams::pgl { return ::pglEnableApplicationAllThreadDumpOnCrash(enabled); } - Result TriggerSnapShotDumper(const char *arg, SnapShotDumpType dump_type) { - return ::pglTriggerSnapShotDumper(arg, static_cast<::PglSnapShotDumpType>(dump_type)); + Result TriggerApplicationSnapShotDumper(const char *arg, SnapShotDumpType dump_type) { + return ::pglTriggerApplicationSnapShotDumper(static_cast<::PglSnapShotDumpType>(dump_type), arg); } Result GetEventObserver(pgl::EventObserver *out) { diff --git a/libraries/libstratosphere/source/pgl/srv/pgl_srv_api.cpp b/libraries/libstratosphere/source/pgl/srv/pgl_srv_api.cpp new file mode 100644 index 000000000..330d11550 --- /dev/null +++ b/libraries/libstratosphere/source/pgl/srv/pgl_srv_api.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "pgl_srv_shell.hpp" + +namespace ams::pgl::srv { + + void Initialize(ShellInterface *interface, MemoryResource *mr) { + /* Set the memory resource for the interface. */ + interface->Initialize(mr); + + /* 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::EnableApplicationExtraThread(); + } + + /* Start the Process Tracking thread. */ + pgl::srv::InitializeProcessControlTask(); + } + + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp new file mode 100644 index 000000000..9529f26e0 --- /dev/null +++ b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp @@ -0,0 +1,485 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#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(std::is_pod::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), + }; + + bool g_is_production = true; + bool g_enable_crash_report_screenshot = true; + bool g_enable_jit_debug = false; + + constexpr inline size_t ProcessControlTaskStackSize = 8_KB; + constexpr inline s32 ProcessControlTaskPriority = 21; + os::ThreadType g_process_control_task_thread; + alignas(os::ThreadStackAlignment) u8 g_process_control_task_stack[ProcessControlTaskStackSize]; + + os::SdkMutex g_observer_list_mutex; + util::IntrusiveListBaseTraits::ListType g_observer_list; + + os::SdkMutex g_process_data_mutex; + ProcessData g_process_data[ProcessDataCount]; + + 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; + } + } + + std::optional GetRunningApplicationProcessId() { + os::ProcessId process_id; + if (R_SUCCEEDED(pm::shell::GetApplicationProcessIdForShell(std::addressof(process_id)))) { + return process_id; + } else { + return std::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 std::snprintf(dst, dst_size, "D %010llu \"%s\" -log %d -dump %d", static_cast(static_cast(process_id)), str_arg, log_arg, dump_arg); + } else { + return std::snprintf(dst, dst_size, "D %010llu -log %d -dump %d", static_cast(static_cast(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 dummy_process_id; + return pm::shell::LaunchProgram(std::addressof(dummy_process_id), ncm::ProgramLocation::Make(ncm::SystemDebugAppletId::SnapShotDumper, ncm::StorageId::BuiltInSystem), pm::LaunchFlags_None); + } + + 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) { + static os::ProcessId s_crashed_process_id = os::InvalidProcessId; + static os::ProcessId s_creport_process_id = os::InvalidProcessId; + + /* If the program that crashed is creport, we should just terminate both processes and return. */ + if (process_id == s_creport_process_id) { + TerminateProcess(s_crashed_process_id); + TerminateProcess(s_creport_process_id); + s_crashed_process_id = os::InvalidProcessId; + s_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 = std::snprintf(arguments, sizeof(arguments), "%ld %d %d", static_cast(static_cast(process_id)), GetCrashReportDetailedArgument(data_flags), GetCrashReportScreenShotArgument(data_flags)); + 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. */ + s_crashed_process_id = process_id; + s_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(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(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(event_info.event)) { + case pm::ProcessEvent::None: + continue_getting_event = false; + break; + case pm::ProcessEvent::Exited: + 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), ProcessControlTaskPriority)); + + /* 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; + return ResultSuccess(); + } + + Result TerminateProcess(os::ProcessId process_id) { + /* Ask PM to terminate the process. */ + 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; + return ResultSuccess(); + } + + Result BoostSystemMemoryResourceLimit(u64 size) { + /* Ask PM to boost the limit. */ + 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. */ + return TriggerSnapShotDumper(process_id, dump_type, arg); + } + +} diff --git a/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.hpp b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.hpp new file mode 100644 index 000000000..f9c4c93d7 --- /dev/null +++ b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +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); + +} diff --git a/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.cpp b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.cpp new file mode 100644 index 000000000..daaa1e0f5 --- /dev/null +++ b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "pgl_srv_shell_event_observer.hpp" +#include "pgl_srv_shell.hpp" + +namespace ams::pgl::srv { + + ShellEventObserver::ShellEventObserver() : message_queue(queue_buffer, QueueCapacity), event(os::EventClearMode_AutoClear, true) { + this->heap_handle = lmem::CreateUnitHeap(this->event_info_data, sizeof(this->event_info_data), sizeof(this->event_info_data[0]), lmem::CreateOption_ThreadSafe, 8, GetPointer(this->heap_head)); + + new (GetPointer(this->holder)) ShellEventObserverHolder(this); + RegisterShellEventObserver(GetPointer(this->holder)); + } + + ShellEventObserver::~ShellEventObserver() { + UnregisterShellEventObserver(GetPointer(this->holder)); + GetReference(this->holder).~ShellEventObserverHolder(); + } + + Result ShellEventObserver::PopEventInfo(pm::ProcessEventInfo *out) { + /* Receive an info from the queue. */ + uintptr_t info_address; + R_UNLESS(this->message_queue.TryReceive(std::addressof(info_address)), pgl::ResultNotAvailable()); + pm::ProcessEventInfo *info = reinterpret_cast(info_address); + + /* Set the output. */ + *out = *info; + + /* Free the received info. */ + lmem::FreeToUnitHeap(this->heap_handle, info); + + return ResultSuccess(); + } + + void ShellEventObserver::Notify(const pm::ProcessEventInfo &info) { + /* Allocate a new info. */ + auto allocated = reinterpret_cast(lmem::AllocateFromUnitHeap(this->heap_handle)); + if (!allocated) { + return; + } + + /* Set it to the notification. */ + *allocated = info; + + /* Try to send it. */ + if (!this->message_queue.TrySend(reinterpret_cast(allocated))) { + lmem::FreeToUnitHeap(this->heap_handle, allocated); + return; + } + + /* Notify that we have a new info available. */ + this->event.Signal(); + } + + Result EventObserverInterface::GetProcessEventHandle(ams::sf::OutCopyHandle out) { + out.SetValue(GetReference(this->observer).GetEvent().GetReadableHandle()); + return ResultSuccess(); + } + + Result EventObserverInterface::GetProcessEventInfo(ams::sf::Out out) { + return GetReference(this->observer).PopEventInfo(out.GetPointer()); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.hpp b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.hpp new file mode 100644 index 000000000..29918a8c3 --- /dev/null +++ b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_event_observer.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::pgl::srv { + + class IShellEventObserver { + public: + virtual void Notify(const pm::ProcessEventInfo &info) = 0; + }; + + class ShellEventObserverHolder : public util::IntrusiveListBaseNode { + private: + IShellEventObserver *observer; + public: + explicit ShellEventObserverHolder(IShellEventObserver *observer) : observer(observer) { /* ... */ } + + void Notify(const pm::ProcessEventInfo &info) { + this->observer->Notify(info); + } + }; + + class ShellEventObserver final : public IShellEventObserver { + private: + static constexpr size_t QueueCapacity = 0x20; + private: + os::MessageQueue message_queue; + uintptr_t queue_buffer[QueueCapacity]; + os::SystemEvent event; + TYPED_STORAGE(lmem::HeapCommonHead) heap_head; + lmem::HeapHandle heap_handle; + pm::ProcessEventInfo event_info_data[QueueCapacity]; + TYPED_STORAGE(ShellEventObserverHolder) holder; + public: + ShellEventObserver(); + ~ShellEventObserver(); + + os::SystemEvent &GetEvent() { + return this->event; + } + + Result PopEventInfo(pm::ProcessEventInfo *out); + + virtual void Notify(const pm::ProcessEventInfo &info) override final; + }; + + class EventObserverInterface final : public pgl::sf::IEventObserver { + private: + TYPED_STORAGE(ShellEventObserver) observer; + public: + EventObserverInterface() { + std::memset(std::addressof(this->observer), 0, sizeof(this->observer)); + new (GetPointer(this->observer)) ShellEventObserver; + } + + ~EventObserverInterface() { + GetReference(this->observer).~ShellEventObserver(); + } + public: + virtual Result GetProcessEventHandle(ams::sf::OutCopyHandle out) override final; + virtual Result GetProcessEventInfo(ams::sf::Out out) override final; + }; + +} diff --git a/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_host_utils.cpp b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_host_utils.cpp new file mode 100644 index 000000000..95aa6d23c --- /dev/null +++ b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_host_utils.cpp @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#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 { + static char to_upper(char c) { + return std::toupper(static_cast(c)); + } + static bool eq(char c1, char c2) { + return to_upper(c1) == to_upper(c2); + } + static bool lt(char c1, char c2) { + return to_upper(c1) < to_upper(c2); + } + static 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 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 = std::basic_string_view; + + 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 content_path[fs::EntryNameLengthMax] = {}; + ExtensionType extension_type = ExtensionType::None; + char mount_name[fs::MountNameLengthMax] = {}; + bool is_mounted = false; + ncm::AutoBuffer content_meta_buffer; + ncm::ProgramId program_id = ncm::InvalidProgramId; + u32 program_version = 0; + ncm::ContentMetaType content_meta_type = static_cast(0); + u8 program_index = 0; + public: + HostPackageReader() : content_meta_buffer() { /* ... */ } + ~HostPackageReader() { + if (this->is_mounted) { + fs::Unmount(this->mount_name); + } + } + + Result Initialize(const char *package, const char *mount) { + /* Copy in the content path. */ + R_UNLESS(strlen(package) <= sizeof(this->content_path) - 1, pgl::ResultBufferNotEnough()); + std::strcpy(this->content_path, package); + + /* Set the extension type. */ + R_TRY(this->SetExtensionType()); + + /* Copy in mount name. */ + R_UNLESS(strlen(mount) <= sizeof(this->mount_name) - 1, pgl::ResultBufferNotEnough()); + std::strcpy(this->mount_name, mount); + + /* Mount the package. */ + R_TRY(fs::MountApplicationPackage(this->mount_name, this->content_path)); + this->is_mounted = true; + + /* Set the content meta buffer. */ + R_TRY(this->SetContentMetaBuffer()); + + /* Ensure we have a content meta buffer. */ + R_UNLESS(this->content_meta_buffer.Get() != nullptr, pgl::ResultContentMetaNotFound()); + + return ResultSuccess(); + } + + Result ReadProgramInfo() { + /* First, read the program index. */ + R_TRY(this->GetProgramIndex(std::addressof(this->program_index))); + + /* Next, create a key for the rest of the fields. */ + const auto key = ncm::PackagedContentMetaReader(this->content_meta_buffer.Get(), this->content_meta_buffer.GetSize()).GetKey(); + + /* Set fields. */ + this->program_id = {key.id}; + this->program_version = key.version; + this->content_meta_type = key.type; + return ResultSuccess(); + } + + Result GetContentPath(lr::Path *out, ncm::ContentType type, std::optional index) const { + switch (this->extension_type) { + case ExtensionType::Nsp: return this->GetContentPathInNsp(out, type, index); + case ExtensionType::Nspd: return this->GetContentPathInNspd(out, type, index); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + ncm::ProgramId GetProgramId() const { + return this->program_id; + } + + u32 GetProgramVersion() const { + return this->program_version; + } + + ncm::ContentMetaType GetContentMetaType() const { + return this->content_meta_type; + } + + u8 GetProgramIndex() const { + return this->program_index; + } + private: + Result GetContentPathInNsp(lr::Path *out, ncm::ContentType type, std::optional index) const { + /* Create a reader. */ + auto reader = ncm::PackagedContentMetaReader(this->content_meta_buffer.Get(), this->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 = std::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()); + + return ResultSuccess(); + } + + Result GetContentPathInNspd(lr::Path *out, ncm::ContentType type, std::optional index) const { + /* 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 = std::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()); + + return ResultSuccess(); + } + + Result GetProgramIndex(u8 *out) { + /* Nspd programs do not have indices. */ + if (this->extension_type == ExtensionType::Nspd) { + *out = 0; + return ResultSuccess(); + } + + /* Create a reader. */ + auto reader = ncm::PackagedContentMetaReader(this->content_meta_buffer.Get(), this->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(); + return ResultSuccess(); + } + + Result SetExtensionType() { + /* First, clear the suffix if the path is a program ncd. */ + if (HasSuffix(this->content_path, "program0.ncd/")) { + this->content_path[strnlen(this->content_path, sizeof(this->content_path)) - std::strlen("program0.ncd/")] = 0; + } + + if (HasSuffix(this->content_path, ".nsp")) { + this->extension_type = ExtensionType::Nsp; + return ResultSuccess(); + } else if (HasSuffix(this->content_path, ".nspd") || HasSuffix(this->content_path, ".nspd/")) { + this->extension_type = ExtensionType::Nspd; + return ResultSuccess(); + } else { + return 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 (this->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. */ + return ncm::ReadContentMetaPath(std::addressof(this->content_meta_buffer), meta_path.str); + } + + Result SearchContent(bool *out, lr::Path *out_path, const char *extension, fs::OpenDirectoryMode mode) const { + /* Generate the root directory path. */ + char root_dir[sizeof(this->mount_name) + 2]; + std::snprintf(root_dir, sizeof(root_dir), "%s:/", this->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 = std::snprintf(out_path->str, sizeof(out_path->str), "%s/%s", this->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; + } + } + + return ResultSuccess(); + } + } + + /* We didn't find a match. */ + *out = false; + return ResultSuccess(); + } + }; + + } + + 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. */ + 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(), + }; + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_host_utils.hpp b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_host_utils.hpp new file mode 100644 index 000000000..b29075438 --- /dev/null +++ b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_host_utils.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +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); + +} diff --git a/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_interface.cpp b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_interface.cpp new file mode 100644 index 000000000..614488e25 --- /dev/null +++ b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell_interface.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "pgl_srv_shell.hpp" +#include "pgl_srv_shell_event_observer.hpp" +#include "pgl_srv_shell_host_utils.hpp" + +namespace ams::pgl::srv { + + Result ShellInterface::LaunchProgram(ams::sf::Out out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags) { + return pgl::srv::LaunchProgram(out.GetPointer(), loc, pm_flags, pgl_flags); + } + + Result ShellInterface::TerminateProcess(os::ProcessId process_id) { + return pgl::srv::TerminateProcess(process_id); + } + + Result ShellInterface::LaunchProgramFromHost(ams::sf::Out out, const ams::sf::InBuffer &content_path, u32 pm_flags) { + return pgl::srv::LaunchProgramFromHost(out.GetPointer(), reinterpret_cast(content_path.GetPointer()), pm_flags); + } + + Result ShellInterface::GetHostContentMetaInfo(ams::sf::Out out, const ams::sf::InBuffer &content_path) { + return pgl::srv::GetHostContentMetaInfo(out.GetPointer(), reinterpret_cast(content_path.GetPointer())); + } + + Result ShellInterface::GetApplicationProcessId(ams::sf::Out out) { + return pgl::srv::GetApplicationProcessId(out.GetPointer()); + } + + Result ShellInterface::BoostSystemMemoryResourceLimit(u64 size) { + return pgl::srv::BoostSystemMemoryResourceLimit(size); + } + + Result ShellInterface::IsProcessTracked(ams::sf::Out out, os::ProcessId process_id) { + out.SetValue(pgl::srv::IsProcessTracked(process_id)); + return ResultSuccess(); + } + + Result ShellInterface::EnableApplicationCrashReport(bool enabled) { + pgl::srv::EnableApplicationCrashReport(enabled); + return ResultSuccess(); + } + + Result ShellInterface::IsApplicationCrashReportEnabled(ams::sf::Out out) { + out.SetValue(pgl::srv::IsApplicationCrashReportEnabled()); + return ResultSuccess(); + } + + Result ShellInterface::EnableApplicationAllThreadDumpOnCrash(bool enabled) { + pgl::srv::EnableApplicationAllThreadDumpOnCrash(enabled); + return ResultSuccess(); + } + + Result ShellInterface::TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const ams::sf::InBuffer &arg) { + return pgl::srv::TriggerApplicationSnapShotDumper(dump_type, reinterpret_cast(arg.GetPointer())); + } + + Result ShellInterface::GetShellEventObserver(ams::sf::Out> out) { + /* Allocate a new interface. */ + auto *observer_memory = this->memory_resource->Allocate(sizeof(EventObserverInterface), alignof(EventObserverInterface)); + AMS_ABORT_UNLESS(observer_memory != nullptr); + + /* Create the interface object. */ + new (observer_memory) EventObserverInterface; + + /* Set the output. */ + out.SetValue(std::shared_ptr(reinterpret_cast(observer_memory), [&](EventObserverInterface *obj) { + /* Destroy the object. */ + obj->~EventObserverInterface(); + + /* Custom deleter: use the memory resource to free. */ + this->memory_resource->Deallocate(obj, sizeof(EventObserverInterface), alignof(EventObserverInterface)); + })); + return ResultSuccess(); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/pm/pm_shell_api.cpp b/libraries/libstratosphere/source/pm/pm_shell_api.cpp index c6a0b2f5e..dd3155edd 100644 --- a/libraries/libstratosphere/source/pm/pm_shell_api.cpp +++ b/libraries/libstratosphere/source/pm/pm_shell_api.cpp @@ -18,10 +18,39 @@ namespace ams::pm::shell { /* Shell API. */ - Result WEAK_SYMBOL LaunchProgram(os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 launch_flags) { + Result WEAK_SYMBOL LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 launch_flags) { static_assert(sizeof(ncm::ProgramLocation) == sizeof(NcmProgramLocation)); static_assert(alignof(ncm::ProgramLocation) == alignof(NcmProgramLocation)); - return pmshellLaunchProgram(launch_flags, reinterpret_cast(&loc), reinterpret_cast(out_process_id)); + return pmshellLaunchProgram(launch_flags, reinterpret_cast(&loc), reinterpret_cast(out)); + } + + Result TerminateProcess(os::ProcessId process_id) { + return ::pmshellTerminateProcess(static_cast(process_id)); + } + + Result GetProcessEventEvent(os::SystemEvent *out) { + ::Event evt; + R_TRY(::pmshellGetProcessEventHandle(std::addressof(evt))); + out->Attach(evt.revent, true, svc::InvalidHandle, false, os::EventClearMode_ManualClear); + return ResultSuccess(); + } + + Result GetProcessEventInfo(ProcessEventInfo *out) { + static_assert(sizeof(*out) == sizeof(::PmProcessEventInfo)); + return ::pmshellGetProcessEventInfo(reinterpret_cast<::PmProcessEventInfo *>(out)); + } + + Result GetApplicationProcessIdForShell(os::ProcessId *out) { + static_assert(sizeof(*out) == sizeof(u64)); + return ::pmshellGetApplicationProcessIdForShell(reinterpret_cast(out)); + } + + Result BoostSystemMemoryResourceLimit(u64 size) { + return ::pmshellBoostSystemMemoryResourceLimit(size); + } + + Result EnableApplicationExtraThread() { + return ::pmshellEnableApplicationExtraThread(); } } diff --git a/libraries/libstratosphere/source/settings/impl/settings_error_report_impl.cpp b/libraries/libstratosphere/source/settings/impl/settings_error_report_impl.cpp new file mode 100644 index 000000000..26e8ce588 --- /dev/null +++ b/libraries/libstratosphere/source/settings/impl/settings_error_report_impl.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "settings_error_report_impl.hpp" + +namespace ams::settings::impl { + + Result GetErrorReportSharePermission(s32 *out) { + static_assert(sizeof(*out) == sizeof(::SetSysErrorReportSharePermission)); + return ::setsysGetErrorReportSharePermission(reinterpret_cast<::SetSysErrorReportSharePermission *>(out)); + } + +} diff --git a/libraries/libstratosphere/source/settings/impl/settings_error_report_impl.hpp b/libraries/libstratosphere/source/settings/impl/settings_error_report_impl.hpp new file mode 100644 index 000000000..6f791b17d --- /dev/null +++ b/libraries/libstratosphere/source/settings/impl/settings_error_report_impl.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::settings::impl { + + Result GetErrorReportSharePermission(s32 *out); + +} diff --git a/libraries/libstratosphere/source/settings/settings_error_report.cpp b/libraries/libstratosphere/source/settings/settings_error_report.cpp new file mode 100644 index 000000000..c11d2936d --- /dev/null +++ b/libraries/libstratosphere/source/settings/settings_error_report.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/settings_error_report_impl.hpp" + +namespace ams::settings::system { + + ErrorReportSharePermission GetErrorReportSharePermission() { + s32 perm = 0; + R_ABORT_UNLESS(settings::impl::GetErrorReportSharePermission(std::addressof(perm))); + return static_cast(perm); + } + +} diff --git a/libraries/libstratosphere/source/settings/settings_fwdbg_api.cpp b/libraries/libstratosphere/source/settings/settings_fwdbg_api.cpp index 31e6b1951..9efd12e8a 100644 --- a/libraries/libstratosphere/source/settings/settings_fwdbg_api.cpp +++ b/libraries/libstratosphere/source/settings/settings_fwdbg_api.cpp @@ -17,8 +17,11 @@ namespace ams::settings::fwdbg { - /* TODO: Implement when libnx wrapper is added. */ - bool IsDebugModeEnabled(); + bool IsDebugModeEnabled() { + bool value = false; + R_ABORT_UNLESS(::setsysGetDebugModeFlag(std::addressof(value))); + return value; + } size_t WEAK_SYMBOL GetSettingsItemValueSize(const char *name, const char *key) { u64 size = 0; diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index 77c762b67..aed86ea51 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/results/pgl_results.hpp b/libraries/libvapours/include/vapours/results/pgl_results.hpp new file mode 100644 index 000000000..93e4eb1a4 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/pgl_results.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::pgl { + + R_DEFINE_NAMESPACE_RESULT_MODULE(228); + + R_DEFINE_ERROR_RESULT(NotAvailable, 2); + R_DEFINE_ERROR_RESULT(ApplicationNotRunning, 3); + R_DEFINE_ERROR_RESULT(BufferNotEnough, 4); + R_DEFINE_ERROR_RESULT(ApplicationContentNotFound, 5); + R_DEFINE_ERROR_RESULT(ContentMetaNotFound, 6); + +} diff --git a/stratosphere/pgl/Makefile b/stratosphere/pgl/Makefile new file mode 100644 index 000000000..7320b9f99 --- /dev/null +++ b/stratosphere/pgl/Makefile @@ -0,0 +1,128 @@ +#--------------------------------------------------------------------------------- +# pull in common stratosphere sysmodule configuration +#--------------------------------------------------------------------------------- +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + + +CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ + $(notdir $(wildcard $(dir)/*.c)))) +CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) +CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) +CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) + +CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ + $(notdir $(wildcard $(dir)/*.cpp)))) +CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) +CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) +CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) + +SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ + $(notdir $(wildcard $(dir)/*.s)))) +SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) +SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) +SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) + +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).nsp + +ifeq ($(strip $(APP_JSON)),) +$(OUTPUT).nsp : $(OUTPUT).nso +else +$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm +endif + +$(OUTPUT).nso : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/stratosphere/pgl/pgl.json b/stratosphere/pgl/pgl.json new file mode 100644 index 000000000..4b813d942 --- /dev/null +++ b/stratosphere/pgl/pgl.json @@ -0,0 +1,88 @@ +{ + "name": "pgl", + "title_id": "0x0100000000000042", + "title_id_range_min": "0x0100000000000042", + "title_id_range_max": "0x0100000000000042", + "main_thread_stack_size": "0x00004000", + "main_thread_priority": 49, + "default_cpu_id": 3, + "process_category": 0, + "is_retail": true, + "pool_partition": 2, + "is_64_bit": true, + "address_space_type": 3, + "filesystem_access": { + "permissions": "0xFFFFFFFFFFFFFFFF" + }, + "service_access": ["erpt:c", "fatal:u", "fsp-srv", "ldr:shel", "lm", "lr", "pm:shell", "set", "set:sys"], + "service_host": ["pgl"], + "kernel_capabilities": [{ + "type": "kernel_flags", + "value": { + "highest_thread_priority": 63, + "lowest_thread_priority": 24, + "lowest_cpu_id": 3, + "highest_cpu_id": 3 + } + }, { + "type": "syscalls", + "value": { + "svcSetHeapSize": "0x01", + "svcSetMemoryPermission": "0x02", + "svcSetMemoryAttribute": "0x03", + "svcMapMemory": "0x04", + "svcUnmapMemory": "0x05", + "svcQueryMemory": "0x06", + "svcExitProcess": "0x07", + "svcCreateThread": "0x08", + "svcStartThread": "0x09", + "svcExitThread": "0x0a", + "svcSleepThread": "0x0b", + "svcGetThreadPriority": "0x0c", + "svcSetThreadPriority": "0x0d", + "svcGetThreadCoreMask": "0x0e", + "svcSetThreadCoreMask": "0x0f", + "svcGetCurrentProcessorNumber": "0x10", + "svcSignalEvent": "0x11", + "svcClearEvent": "0x12", + "svcMapSharedMemory": "0x13", + "svcUnmapSharedMemory": "0x14", + "svcCreateTransferMemory": "0x15", + "svcCloseHandle": "0x16", + "svcResetSignal": "0x17", + "svcWaitSynchronization": "0x18", + "svcCancelSynchronization": "0x19", + "svcArbitrateLock": "0x1a", + "svcArbitrateUnlock": "0x1b", + "svcWaitProcessWideKeyAtomic": "0x1c", + "svcSignalProcessWideKey": "0x1d", + "svcGetSystemTick": "0x1e", + "svcConnectToNamedPort": "0x1f", + "svcSendSyncRequestLight": "0x20", + "svcSendSyncRequest": "0x21", + "svcSendSyncRequestWithUserBuffer": "0x22", + "svcSendAsyncRequestWithUserBuffer": "0x23", + "svcGetProcessId": "0x24", + "svcGetThreadId": "0x25", + "svcBreak": "0x26", + "svcOutputDebugString": "0x27", + "svcReturnFromException": "0x28", + "svcGetInfo": "0x29", + "svcWaitForAddress": "0x34", + "svcSignalToAddress": "0x35", + "svcCreateSession": "0x40", + "svcAcceptSession": "0x41", + "svcReplyAndReceiveLight": "0x42", + "svcReplyAndReceive": "0x43", + "svcReplyAndReceiveWithUserBuffer": "0x44", + "svcCreateEvent": "0x45", + "svcCallSecureMonitor": "0x7F" + } + }, { + "type": "min_kernel_version", + "value": "0x0091" + }, { + "type": "handle_table_size", + "value": 256 + }] +} \ No newline at end of file diff --git a/stratosphere/pgl/source/pgl_main.cpp b/stratosphere/pgl/source/pgl_main.cpp new file mode 100644 index 000000000..69b90ed27 --- /dev/null +++ b/stratosphere/pgl/source/pgl_main.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +extern "C" { + extern u32 __start__; + + u32 __nx_applet_type = AppletType_None; + u32 __nx_fs_num_sessions = 1; + + #define INNER_HEAP_SIZE 0x4000 + size_t nx_inner_heap_size = INNER_HEAP_SIZE; + char nx_inner_heap[INNER_HEAP_SIZE]; + + void __libnx_initheap(void); + void __appInit(void); + void __appExit(void); + + /* Exception handling. */ + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; + u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); + void __libnx_exception_handler(ThreadExceptionDump *ctx); +} + +namespace ams { + + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Pgl; + + namespace result { + + bool CallFatalOnResultAssertion = false; + + } + +} + +using namespace ams; + +void __libnx_exception_handler(ThreadExceptionDump *ctx) { + ams::CrashHandler(ctx); +} + +namespace ams::pgl { + + namespace { + + /* pgl. */ + constexpr size_t NumServers = 1; + ams::sf::hipc::ServerManager g_server_manager; + + constexpr sm::ServiceName ShellServiceName = sm::ServiceName::Encode("pgl"); + constexpr size_t ShellMaxSessions = 8; /* Official maximum is 8. */ + + /* TODO: C++20 constinit */ pgl::srv::ShellInterface g_shell_interface; + + ALWAYS_INLINE std::shared_ptr GetSharedPointerToShellInterface() { + return ams::sf::ServiceObjectTraits::SharedPointerHelper::GetEmptyDeleteSharedPointer(std::addressof(g_shell_interface)); + } + + void RegisterServiceSession() { + R_ABORT_UNLESS(g_server_manager.RegisterServer(ShellServiceName, ShellMaxSessions, GetSharedPointerToShellInterface())); + } + + void LoopProcess() { + g_server_manager.LoopProcess(); + } + + /* NOTE: Nintendo reserves only 0x2000 bytes for this 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). */ + /* We should have a smaller memory footprint than N in the end, regardless. */ + u8 g_heap_memory[32_KB]; + TYPED_STORAGE(ams::sf::ExpHeapMemoryResource) g_heap_memory_resource; + + void *Allocate(size_t size) { + return lmem::AllocateFromExpHeap(GetReference(g_heap_memory_resource).GetHandle(), size); + } + + void Deallocate(void *p, size_t size) { + return lmem::FreeToExpHeap(GetReference(g_heap_memory_resource).GetHandle(), p); + } + + void InitializeHeap() { + auto heap_handle = lmem::CreateExpHeap(g_heap_memory, sizeof(g_heap_memory), lmem::CreateOption_ThreadSafe); + new (GetPointer(g_heap_memory_resource)) ams::sf::ExpHeapMemoryResource(heap_handle); + } + + } + +} + +void __libnx_initheap(void) { + void* addr = nx_inner_heap; + size_t size = nx_inner_heap_size; + + /* Newlib */ + extern char* fake_heap_start; + extern char* fake_heap_end; + + fake_heap_start = (char*)addr; + fake_heap_end = (char*)addr + size; + + ams::pgl::InitializeHeap(); +} + +void __appInit(void) { + hos::SetVersionForLibnx(); + + fs::SetAllocator(pgl::Allocate, pgl::Deallocate); + + sm::DoWithSession([&]() { + R_ABORT_UNLESS(setInitialize()); + R_ABORT_UNLESS(setsysInitialize()); + R_ABORT_UNLESS(pmshellInitialize()); + R_ABORT_UNLESS(ldrShellInitialize()); + R_ABORT_UNLESS(lrInitialize()); + R_ABORT_UNLESS(fsInitialize()); + }); + + ams::CheckApiVersion(); +} + +void __appExit(void) { + fsExit(); + lrExit(); + ldrShellExit(); + pmshellExit(); + setsysExit(); + setExit(); +} + + +int main(int argc, char **argv) +{ + /* Register the pgl service. */ + pgl::RegisterServiceSession(); + + /* Initialize the server library. */ + pgl::srv::Initialize(std::addressof(pgl::g_shell_interface), GetPointer(pgl::g_heap_memory_resource)); + + /* Loop forever, servicing our services. */ + pgl::LoopProcess(); + + /* Cleanup */ + return 0; +} + diff --git a/stratosphere/pm/source/impl/pm_process_manager.cpp b/stratosphere/pm/source/impl/pm_process_manager.cpp index 21a80595f..bc8892e49 100644 --- a/stratosphere/pm/source/impl/pm_process_manager.cpp +++ b/stratosphere/pm/source/impl/pm_process_manager.cpp @@ -34,26 +34,6 @@ namespace ams::pm::impl { u32 flags; }; - enum LaunchFlags { - LaunchFlags_None = 0, - LaunchFlags_SignalOnExit = (1 << 0), - LaunchFlags_SignalOnStart = (1 << 1), - LaunchFlags_SignalOnException = (1 << 2), - LaunchFlags_SignalOnDebugEvent = (1 << 3), - LaunchFlags_StartSuspended = (1 << 4), - LaunchFlags_DisableAslr = (1 << 5), - }; - - enum LaunchFlagsDeprecated { - LaunchFlagsDeprecated_None = 0, - LaunchFlagsDeprecated_SignalOnExit = (1 << 0), - LaunchFlagsDeprecated_StartSuspended = (1 << 1), - LaunchFlagsDeprecated_SignalOnException = (1 << 2), - LaunchFlagsDeprecated_DisableAslr = (1 << 3), - LaunchFlagsDeprecated_SignalOnDebugEvent = (1 << 4), - LaunchFlagsDeprecated_SignalOnStart = (1 << 5), - }; - #define GET_FLAG_MASK(flag) (hos_version >= hos::Version_5_0_0 ? static_cast(LaunchFlags_##flag) : static_cast(LaunchFlagsDeprecated_##flag)) inline bool ShouldSignalOnExit(u32 launch_flags) {