mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-18 00:12:03 +00:00
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
This commit is contained in:
parent
f2944d36ba
commit
98cc051387
42 changed files with 2218 additions and 57 deletions
|
@ -16,5 +16,6 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ldr/ldr_types.hpp"
|
#include <stratosphere/ldr/ldr_types.hpp>
|
||||||
#include "ldr/ldr_pm_api.hpp"
|
#include <stratosphere/ldr/ldr_shell_api.hpp>
|
||||||
|
#include <stratosphere/ldr/ldr_pm_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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||||
|
#include <stratosphere/ldr/ldr_types.hpp>
|
||||||
|
|
||||||
|
namespace ams::ldr {
|
||||||
|
|
||||||
|
/* Shell API. */
|
||||||
|
Result InitializeForShell();
|
||||||
|
Result FinalizeForShell();
|
||||||
|
|
||||||
|
Result SetProgramArgument(ncm::ProgramId program_id, const void *arg, size_t size);
|
||||||
|
Result FlushArguments();
|
||||||
|
|
||||||
|
}
|
|
@ -402,6 +402,34 @@ namespace ams::ncm {
|
||||||
return true;
|
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 {
|
struct LibraryAppletId {
|
||||||
u64 value;
|
u64 value;
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <stratosphere/os/os_random.hpp>
|
#include <stratosphere/os/os_random.hpp>
|
||||||
#include <stratosphere/os/os_mutex.hpp>
|
#include <stratosphere/os/os_mutex.hpp>
|
||||||
#include <stratosphere/os/os_condition_variable.hpp>
|
#include <stratosphere/os/os_condition_variable.hpp>
|
||||||
|
#include <stratosphere/os/os_sdk_mutex.hpp>
|
||||||
#include <stratosphere/os/os_rw_lock.hpp>
|
#include <stratosphere/os/os_rw_lock.hpp>
|
||||||
#include <stratosphere/os/os_semaphore.hpp>
|
#include <stratosphere/os/os_semaphore.hpp>
|
||||||
#include <stratosphere/os/os_event.hpp>
|
#include <stratosphere/os/os_event.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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/os/impl/os_internal_critical_section.hpp>
|
||||||
|
|
||||||
|
namespace ams::os {
|
||||||
|
|
||||||
|
class SdkConditionVariable;
|
||||||
|
|
||||||
|
struct SdkMutexType {
|
||||||
|
union {
|
||||||
|
s32 _arr[sizeof(impl::InternalCriticalSectionStorage) / sizeof(s32)];
|
||||||
|
impl::InternalCriticalSectionStorage _storage;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
static_assert(std::is_trivial<SdkMutexType>::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(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -19,3 +19,5 @@
|
||||||
#include <stratosphere/pgl/pgl_types.hpp>
|
#include <stratosphere/pgl/pgl_types.hpp>
|
||||||
#include <stratosphere/pgl/pgl_event_observer.hpp>
|
#include <stratosphere/pgl/pgl_event_observer.hpp>
|
||||||
#include <stratosphere/pgl/pgl_shell_api.hpp>
|
#include <stratosphere/pgl/pgl_shell_api.hpp>
|
||||||
|
#include <stratosphere/pgl/pgl_shell_api.hpp>
|
||||||
|
#include <stratosphere/pgl/srv/pgl_srv_api.hpp>
|
||||||
|
|
|
@ -34,7 +34,7 @@ namespace ams::pgl {
|
||||||
Result EnableApplicationCrashReport(bool enabled);
|
Result EnableApplicationCrashReport(bool enabled);
|
||||||
Result IsApplicationCrashReportEnabled(bool *out);
|
Result IsApplicationCrashReportEnabled(bool *out);
|
||||||
Result EnableApplicationAllThreadDumpOnCrash(bool enabled);
|
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);
|
Result GetEventObserver(pgl::EventObserver *out);
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
namespace ams::pgl {
|
namespace ams::pgl {
|
||||||
|
|
||||||
enum LaunchFlags : u8 {
|
enum LaunchFlags : u8 {
|
||||||
|
LaunchFlags_None = 0,
|
||||||
LaunchFlags_EnableDetailedCrashReport = (1 << 0),
|
LaunchFlags_EnableDetailedCrashReport = (1 << 0),
|
||||||
LaunchFlags_EnableCrashReportScreenShotForProduction = (1 << 1),
|
LaunchFlags_EnableCrashReportScreenShotForProduction = (1 << 1),
|
||||||
LaunchFlags_EnableCrashReportScreenShotForDevelop = (1 << 2),
|
LaunchFlags_EnableCrashReportScreenShotForDevelop = (1 << 2),
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/os.hpp>
|
||||||
|
#include <stratosphere/pm.hpp>
|
||||||
|
#include <stratosphere/pgl/pgl_types.hpp>
|
||||||
|
#include <stratosphere/pgl/sf/pgl_sf_i_event_observer.hpp>
|
||||||
|
|
||||||
|
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<os::ProcessId> 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<os::ProcessId> out, const ams::sf::InBuffer &content_path, u32 pm_flags) = 0;
|
||||||
|
virtual Result GetHostContentMetaInfo(ams::sf::Out<pgl::ContentMetaInfo> out, const ams::sf::InBuffer &content_path) = 0;
|
||||||
|
virtual Result GetApplicationProcessId(ams::sf::Out<os::ProcessId> out) = 0;
|
||||||
|
virtual Result BoostSystemMemoryResourceLimit(u64 size) = 0;
|
||||||
|
virtual Result IsProcessTracked(ams::sf::Out<bool> out, os::ProcessId process_id) = 0;
|
||||||
|
virtual Result EnableApplicationCrashReport(bool enabled) = 0;
|
||||||
|
virtual Result IsApplicationCrashReportEnabled(ams::sf::Out<bool> 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<std::shared_ptr<pgl::sf::IEventObserver>> 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),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/pgl/pgl_types.hpp>
|
||||||
|
#include <stratosphere/pgl/srv/pgl_srv_shell_interface.hpp>
|
||||||
|
|
||||||
|
namespace ams::pgl::srv {
|
||||||
|
|
||||||
|
void Initialize(ShellInterface *interface, MemoryResource *mr);
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/pgl/pgl_types.hpp>
|
||||||
|
#include <stratosphere/pgl/sf/pgl_sf_i_shell_interface.hpp>
|
||||||
|
|
||||||
|
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<os::ProcessId> 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<os::ProcessId> out, const ams::sf::InBuffer &content_path, u32 pm_flags) override final;
|
||||||
|
virtual Result GetHostContentMetaInfo(ams::sf::Out<pgl::ContentMetaInfo> out, const ams::sf::InBuffer &content_path) override final;
|
||||||
|
virtual Result GetApplicationProcessId(ams::sf::Out<os::ProcessId> out) override final;
|
||||||
|
virtual Result BoostSystemMemoryResourceLimit(u64 size) override final;
|
||||||
|
virtual Result IsProcessTracked(ams::sf::Out<bool> out, os::ProcessId process_id) override final;
|
||||||
|
virtual Result EnableApplicationCrashReport(bool enabled) override final;
|
||||||
|
virtual Result IsApplicationCrashReportEnabled(ams::sf::Out<bool> 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<std::shared_ptr<pgl::sf::IEventObserver>> out) override final;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -23,6 +23,12 @@
|
||||||
namespace ams::pm::shell {
|
namespace ams::pm::shell {
|
||||||
|
|
||||||
/* Shell API. */
|
/* 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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,9 +32,29 @@ namespace ams::pm {
|
||||||
ResourceLimitGroup_Count,
|
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,
|
None = 0,
|
||||||
Exited = 1,
|
Exited = 1,
|
||||||
Started = 2,
|
Started = 2,
|
||||||
|
@ -43,7 +63,7 @@ namespace ams::pm {
|
||||||
DebugBreak = 5,
|
DebugBreak = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ProcessEventDeprecated {
|
enum class ProcessEventDeprecated : u32 {
|
||||||
None = 0,
|
None = 0,
|
||||||
Exception = 1,
|
Exception = 1,
|
||||||
Exited = 2,
|
Exited = 2,
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "settings/settings_types.hpp"
|
#include "settings/settings_types.hpp"
|
||||||
#include "settings/settings_fwdbg_types.hpp"
|
#include "settings/settings_fwdbg_types.hpp"
|
||||||
#include "settings/settings_fwdbg_api.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_firmware_version.hpp"
|
||||||
#include "settings/system/settings_product_model.hpp"
|
#include "settings/system/settings_product_model.hpp"
|
||||||
#include "settings/system/settings_region.hpp"
|
#include "settings/system/settings_region.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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/settings/settings_types.hpp>
|
||||||
|
|
||||||
|
namespace ams::settings::system {
|
||||||
|
|
||||||
|
enum ErrorReportSharePermission {
|
||||||
|
ErrorReportSharePermission_NotConfirmed = 0,
|
||||||
|
ErrorReportSharePermission_Granted = 1,
|
||||||
|
ErrorReportSharePermission_Denied = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
ErrorReportSharePermission GetErrorReportSharePermission();
|
||||||
|
|
||||||
|
}
|
|
@ -16,15 +16,16 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "sf/sf_common.hpp"
|
#include <stratosphere/sf/sf_common.hpp>
|
||||||
#include "sf/sf_mem_utility.hpp"
|
#include <stratosphere/sf/sf_lmem_utility.hpp>
|
||||||
#include "sf/sf_service_object.hpp"
|
#include <stratosphere/sf/sf_mem_utility.hpp>
|
||||||
#include "sf/hipc/sf_hipc_server_session_manager.hpp"
|
#include <stratosphere/sf/sf_service_object.hpp>
|
||||||
|
#include <stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp>
|
||||||
|
|
||||||
#include "sf/sf_out.hpp"
|
#include <stratosphere/sf/sf_out.hpp>
|
||||||
#include "sf/sf_buffers.hpp"
|
#include <stratosphere/sf/sf_buffers.hpp>
|
||||||
#include "sf/impl/sf_impl_command_serialization.hpp"
|
#include <stratosphere/sf/impl/sf_impl_command_serialization.hpp>
|
||||||
|
|
||||||
#include "sf/hipc/sf_hipc_server_manager.hpp"
|
#include <stratosphere/sf/hipc/sf_hipc_server_manager.hpp>
|
||||||
|
|
||||||
#include "sf/sf_mitm_dispatch.h"
|
#include <stratosphere/sf/sf_mitm_dispatch.h>
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere/sf/sf_common.hpp>
|
||||||
|
#include <stratosphere/lmem.hpp>
|
||||||
|
|
||||||
|
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<int>(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<size_t>(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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -44,7 +44,7 @@ namespace ams::diag {
|
||||||
inline void DebugLog(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
inline void DebugLog(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||||
|
|
||||||
#ifdef AMS_ENABLE_DEBUG_PRINT
|
#ifdef AMS_ENABLE_DEBUG_PRINT
|
||||||
os::Mutex g_debug_log_lock;
|
os::Mutex g_debug_log_lock(true);
|
||||||
char g_debug_buffer[0x400];
|
char g_debug_buffer[0x400];
|
||||||
|
|
||||||
void DebugLogImpl(const char *format, ::std::va_list vl) {
|
void DebugLogImpl(const char *format, ::std::va_list vl) {
|
||||||
|
|
|
@ -169,7 +169,7 @@ namespace ams::fssystem {
|
||||||
if (this->size > ideal_size) {
|
if (this->size > ideal_size) {
|
||||||
/* If we do, we need to have a buffer allocated from the heap. */
|
/* If we do, we need to have a buffer allocated from the heap. */
|
||||||
AMS_ASSERT(this->buffer != nullptr);
|
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);
|
const size_t new_size = util::AlignUp(ideal_size, HeapBlockSize);
|
||||||
|
|
||||||
|
|
37
libraries/libstratosphere/source/ldr/ldr_shell_api.cpp
Normal file
37
libraries/libstratosphere/source/ldr/ldr_shell_api.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
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<u64>(program_id), arg, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FlushArguments() {
|
||||||
|
return ::ldrShellFlushArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -28,19 +28,19 @@ namespace ams::lr {
|
||||||
public:
|
public:
|
||||||
/* Actual commands. */
|
/* Actual commands. */
|
||||||
virtual Result ResolveProgramPath(sf::Out<Path> out, ncm::ProgramId id) override {
|
virtual Result ResolveProgramPath(sf::Out<Path> out, ncm::ProgramId id) override {
|
||||||
return lrLrResolveProgramPath(std::addressof(this->srv), static_cast<u64>(id), out->str);
|
return lrLrResolveProgramPath(std::addressof(this->srv), id.value, out->str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result RedirectProgramPath(const Path &path, ncm::ProgramId id) override {
|
virtual Result RedirectProgramPath(const Path &path, ncm::ProgramId id) override {
|
||||||
return lrLrRedirectProgramPath(std::addressof(this->srv), static_cast<u64>(id), path.str);
|
return lrLrRedirectProgramPath(std::addressof(this->srv), id.value, path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result ResolveApplicationControlPath(sf::Out<Path> out, ncm::ProgramId id) override {
|
virtual Result ResolveApplicationControlPath(sf::Out<Path> out, ncm::ProgramId id) override {
|
||||||
return lrLrResolveApplicationControlPath(std::addressof(this->srv), static_cast<u64>(id), out->str);
|
return lrLrResolveApplicationControlPath(std::addressof(this->srv), id.value, out->str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result ResolveApplicationHtmlDocumentPath(sf::Out<Path> out, ncm::ProgramId id) override {
|
virtual Result ResolveApplicationHtmlDocumentPath(sf::Out<Path> out, ncm::ProgramId id) override {
|
||||||
return lrLrResolveApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast<u64>(id), out->str);
|
return lrLrResolveApplicationHtmlDocumentPath(std::addressof(this->srv), id.value, out->str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result ResolveDataPath(sf::Out<Path> out, ncm::DataId id) override {
|
virtual Result ResolveDataPath(sf::Out<Path> out, ncm::DataId id) override {
|
||||||
|
@ -48,31 +48,31 @@ namespace ams::lr {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result RedirectApplicationControlPathDeprecated(const Path &path, ncm::ProgramId id) override {
|
virtual Result RedirectApplicationControlPathDeprecated(const Path &path, ncm::ProgramId id) override {
|
||||||
return lrLrRedirectApplicationControlPath(std::addressof(this->srv), static_cast<u64>(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 {
|
virtual Result RedirectApplicationControlPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override {
|
||||||
return lrLrRedirectApplicationControlPath(std::addressof(this->srv), static_cast<u64>(id), static_cast<u64>(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 {
|
virtual Result RedirectApplicationHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) override {
|
||||||
return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast<u64>(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 {
|
virtual Result RedirectApplicationHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override {
|
||||||
return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast<u64>(id), static_cast<u64>(owner_id), path.str);
|
return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), id.value, owner_id.value, path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result ResolveApplicationLegalInformationPath(sf::Out<Path> out, ncm::ProgramId id) override {
|
virtual Result ResolveApplicationLegalInformationPath(sf::Out<Path> out, ncm::ProgramId id) override {
|
||||||
return lrLrResolveApplicationLegalInformationPath(std::addressof(this->srv), static_cast<u64>(id), out->str);
|
return lrLrResolveApplicationLegalInformationPath(std::addressof(this->srv), id.value, out->str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result RedirectApplicationLegalInformationPathDeprecated(const Path &path, ncm::ProgramId id) override {
|
virtual Result RedirectApplicationLegalInformationPathDeprecated(const Path &path, ncm::ProgramId id) override {
|
||||||
return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), static_cast<u64>(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 {
|
virtual Result RedirectApplicationLegalInformationPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override {
|
||||||
return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), static_cast<u64>(id), static_cast<u64>(owner_id), path.str);
|
return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), id.value, owner_id.value, path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result Refresh() override {
|
virtual Result Refresh() override {
|
||||||
|
@ -100,8 +100,7 @@ namespace ams::lr {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result EraseProgramRedirection(ncm::ProgramId id) override {
|
virtual Result EraseProgramRedirection(ncm::ProgramId id) override {
|
||||||
/* TODO: libnx bindings */
|
return lrLrEraseProgramRedirection(std::addressof(this->srv), id.value);
|
||||||
AMS_ABORT();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result EraseApplicationControlRedirection(ncm::ProgramId id) override {
|
virtual Result EraseApplicationControlRedirection(ncm::ProgramId id) override {
|
||||||
|
|
43
libraries/libstratosphere/source/os/os_sdk_mutex.cpp
Normal file
43
libraries/libstratosphere/source/os/os_sdk_mutex.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -71,8 +71,8 @@ namespace ams::pgl {
|
||||||
return ::pglEnableApplicationAllThreadDumpOnCrash(enabled);
|
return ::pglEnableApplicationAllThreadDumpOnCrash(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result TriggerSnapShotDumper(const char *arg, SnapShotDumpType dump_type) {
|
Result TriggerApplicationSnapShotDumper(const char *arg, SnapShotDumpType dump_type) {
|
||||||
return ::pglTriggerSnapShotDumper(arg, static_cast<::PglSnapShotDumpType>(dump_type));
|
return ::pglTriggerApplicationSnapShotDumper(static_cast<::PglSnapShotDumpType>(dump_type), arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetEventObserver(pgl::EventObserver *out) {
|
Result GetEventObserver(pgl::EventObserver *out) {
|
||||||
|
|
38
libraries/libstratosphere/source/pgl/srv/pgl_srv_api.cpp
Normal file
38
libraries/libstratosphere/source/pgl/srv/pgl_srv_api.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
485
libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp
Normal file
485
libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "pgl_srv_shell.hpp"
|
||||||
|
#include "pgl_srv_shell_event_observer.hpp"
|
||||||
|
|
||||||
|
namespace ams::pgl::srv {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr inline size_t ProcessDataCount = 0x20;
|
||||||
|
|
||||||
|
struct ProcessData {
|
||||||
|
os::ProcessId process_id;
|
||||||
|
u32 flags;
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<ProcessData>::value);
|
||||||
|
|
||||||
|
enum ProcessDataFlag : u32 {
|
||||||
|
ProcessDataFlag_None = 0,
|
||||||
|
ProcessDataFlag_DetailedCrashReportAllowed = (1 << 0),
|
||||||
|
ProcessDataFlag_DetailedCrashReportEnabled = (1 << 1),
|
||||||
|
ProcessDataFlag_HasLogOption = (1 << 2),
|
||||||
|
ProcessDataFlag_OutputAllLog = (1 << 3),
|
||||||
|
ProcessDataFlag_EnableCrashReportScreenShot = (1 << 4),
|
||||||
|
};
|
||||||
|
|
||||||
|
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<ShellEventObserverHolder>::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<os::ProcessId> 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<unsigned long long>(static_cast<u64>(process_id)), str_arg, log_arg, dump_arg);
|
||||||
|
} else {
|
||||||
|
return std::snprintf(dst, dst_size, "D %010llu -log %d -dump %d", static_cast<unsigned long long>(static_cast<u64>(process_id)), log_arg, dump_arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TriggerSnapShotDumper(os::ProcessId process_id, SnapShotDumpType dump_type, const char *arg) {
|
||||||
|
/* Create the arguments. */
|
||||||
|
char process_arguments[800];
|
||||||
|
const size_t arg_len = CreateSnapShotDumpArguments(process_arguments, sizeof(process_arguments), process_id, dump_type, arg);
|
||||||
|
|
||||||
|
/* Set the arguments. */
|
||||||
|
R_TRY(ldr::SetProgramArgument(ncm::SystemDebugAppletId::SnapShotDumper, process_arguments, arg_len + 1));
|
||||||
|
|
||||||
|
/* Launch the process. */
|
||||||
|
os::ProcessId 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<s64>(static_cast<u64>(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<pm::ProcessEvent>(event_info.event) == pm::ProcessEvent::Exited) {
|
||||||
|
HandleExit(event_info.process_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Notify all observers. */
|
||||||
|
std::scoped_lock lk(g_observer_list_mutex);
|
||||||
|
for (auto &observer : g_observer_list) {
|
||||||
|
observer.Notify(event_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the process crashed, handle that. */
|
||||||
|
if (static_cast<pm::ProcessEvent>(event_info.event) == pm::ProcessEvent::Exception) {
|
||||||
|
HandleException(event_info.process_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessControlTask(void *) {
|
||||||
|
/* Get the process event event from pm. */
|
||||||
|
os::SystemEvent process_event;
|
||||||
|
R_ABORT_UNLESS(pm::shell::GetProcessEventEvent(std::addressof(process_event)));
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
/* Wait for an event to come in, and clear our signal. */
|
||||||
|
process_event.Wait();
|
||||||
|
process_event.Clear();
|
||||||
|
|
||||||
|
bool continue_getting_event = true;
|
||||||
|
while (continue_getting_event) {
|
||||||
|
/* Try to get an event info. */
|
||||||
|
pm::ProcessEventInfo event_info;
|
||||||
|
if (R_FAILED(pm::shell::GetProcessEventInfo(std::addressof(event_info)))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process the event. */
|
||||||
|
switch (static_cast<pm::ProcessEvent>(event_info.event)) {
|
||||||
|
case pm::ProcessEvent::None:
|
||||||
|
continue_getting_event = false;
|
||||||
|
break;
|
||||||
|
case pm::ProcessEvent::Exited:
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.hpp
Normal file
38
libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::pgl::srv {
|
||||||
|
|
||||||
|
void InitializeProcessControlTask();
|
||||||
|
|
||||||
|
class ShellEventObserverHolder;
|
||||||
|
|
||||||
|
void RegisterShellEventObserver(ShellEventObserverHolder *holder);
|
||||||
|
void UnregisterShellEventObserver(ShellEventObserverHolder *holder);
|
||||||
|
|
||||||
|
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags);
|
||||||
|
Result TerminateProcess(os::ProcessId process_id);
|
||||||
|
Result GetApplicationProcessId(os::ProcessId *out);
|
||||||
|
Result BoostSystemMemoryResourceLimit(u64 size);
|
||||||
|
bool IsProcessTracked(os::ProcessId process_id);
|
||||||
|
void EnableApplicationCrashReport(bool enabled);
|
||||||
|
bool IsApplicationCrashReportEnabled();
|
||||||
|
void EnableApplicationAllThreadDumpOnCrash(bool enabled);
|
||||||
|
Result TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const char *arg);
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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<pm::ProcessEventInfo *>(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<pm::ProcessEventInfo *>(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<uintptr_t>(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<pm::ProcessEventInfo> out) {
|
||||||
|
return GetReference(this->observer).PopEventInfo(out.GetPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::pgl::srv {
|
||||||
|
|
||||||
|
class IShellEventObserver {
|
||||||
|
public:
|
||||||
|
virtual void Notify(const pm::ProcessEventInfo &info) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShellEventObserverHolder : public util::IntrusiveListBaseNode<ShellEventObserverHolder> {
|
||||||
|
private:
|
||||||
|
IShellEventObserver *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<pm::ProcessEventInfo> out) override final;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "pgl_srv_shell_host_utils.hpp"
|
||||||
|
#include "pgl_srv_shell.hpp"
|
||||||
|
|
||||||
|
namespace ams::pgl::srv {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr inline char HostPackageMountName[] = "HostPackageRead";
|
||||||
|
static_assert(sizeof(HostPackageMountName) - 1 <= fs::MountNameLengthMax);
|
||||||
|
|
||||||
|
struct CaseInsensitiveCharTraits : public std::char_traits<char> {
|
||||||
|
static char to_upper(char c) {
|
||||||
|
return std::toupper(static_cast<unsigned char>(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<char, CaseInsensitiveCharTraits>;
|
||||||
|
|
||||||
|
enum class ExtensionType {
|
||||||
|
None = 0,
|
||||||
|
Nsp = 1,
|
||||||
|
Nspd = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool HasSuffix(const char *str, const char *suffix) {
|
||||||
|
const size_t suffix_len = std::strlen(suffix);
|
||||||
|
const size_t str_len = std::strlen(str);
|
||||||
|
if (suffix_len > str_len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (PathView(str).substr(str_len - suffix_len) == PathView(suffix));
|
||||||
|
}
|
||||||
|
|
||||||
|
class HostPackageReader {
|
||||||
|
NON_COPYABLE(HostPackageReader);
|
||||||
|
NON_MOVEABLE(HostPackageReader);
|
||||||
|
private:
|
||||||
|
char 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<ncm::ContentMetaType>(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<u8> 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<u8> 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<u8> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::pgl::srv {
|
||||||
|
|
||||||
|
Result LaunchProgramFromHost(os::ProcessId *out, const char *content_path, u32 pm_flags);
|
||||||
|
Result GetHostContentMetaInfo(pgl::ContentMetaInfo *out, const char *content_path);
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "pgl_srv_shell.hpp"
|
||||||
|
#include "pgl_srv_shell_event_observer.hpp"
|
||||||
|
#include "pgl_srv_shell_host_utils.hpp"
|
||||||
|
|
||||||
|
namespace ams::pgl::srv {
|
||||||
|
|
||||||
|
Result ShellInterface::LaunchProgram(ams::sf::Out<os::ProcessId> 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<os::ProcessId> out, const ams::sf::InBuffer &content_path, u32 pm_flags) {
|
||||||
|
return pgl::srv::LaunchProgramFromHost(out.GetPointer(), reinterpret_cast<const char *>(content_path.GetPointer()), pm_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ShellInterface::GetHostContentMetaInfo(ams::sf::Out<pgl::ContentMetaInfo> out, const ams::sf::InBuffer &content_path) {
|
||||||
|
return pgl::srv::GetHostContentMetaInfo(out.GetPointer(), reinterpret_cast<const char *>(content_path.GetPointer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ShellInterface::GetApplicationProcessId(ams::sf::Out<os::ProcessId> out) {
|
||||||
|
return pgl::srv::GetApplicationProcessId(out.GetPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ShellInterface::BoostSystemMemoryResourceLimit(u64 size) {
|
||||||
|
return pgl::srv::BoostSystemMemoryResourceLimit(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ShellInterface::IsProcessTracked(ams::sf::Out<bool> 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<bool> 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<const char *>(arg.GetPointer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ShellInterface::GetShellEventObserver(ams::sf::Out<std::shared_ptr<pgl::sf::IEventObserver>> 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<EventObserverInterface>(reinterpret_cast<EventObserverInterface *>(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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -18,10 +18,39 @@
|
||||||
namespace ams::pm::shell {
|
namespace ams::pm::shell {
|
||||||
|
|
||||||
/* Shell API. */
|
/* 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(sizeof(ncm::ProgramLocation) == sizeof(NcmProgramLocation));
|
||||||
static_assert(alignof(ncm::ProgramLocation) == alignof(NcmProgramLocation));
|
static_assert(alignof(ncm::ProgramLocation) == alignof(NcmProgramLocation));
|
||||||
return pmshellLaunchProgram(launch_flags, reinterpret_cast<const NcmProgramLocation *>(&loc), reinterpret_cast<u64 *>(out_process_id));
|
return pmshellLaunchProgram(launch_flags, reinterpret_cast<const NcmProgramLocation *>(&loc), reinterpret_cast<u64 *>(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result TerminateProcess(os::ProcessId process_id) {
|
||||||
|
return ::pmshellTerminateProcess(static_cast<u64>(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<u64 *>(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result BoostSystemMemoryResourceLimit(u64 size) {
|
||||||
|
return ::pmshellBoostSystemMemoryResourceLimit(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result EnableApplicationExtraThread() {
|
||||||
|
return ::pmshellEnableApplicationExtraThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::settings::impl {
|
||||||
|
|
||||||
|
Result GetErrorReportSharePermission(s32 *out);
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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<ErrorReportSharePermission>(perm);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,8 +17,11 @@
|
||||||
|
|
||||||
namespace ams::settings::fwdbg {
|
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) {
|
size_t WEAK_SYMBOL GetSettingsItemValueSize(const char *name, const char *key) {
|
||||||
u64 size = 0;
|
u64 size = 0;
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include <vapours/results/lr_results.hpp>
|
#include <vapours/results/lr_results.hpp>
|
||||||
#include <vapours/results/os_results.hpp>
|
#include <vapours/results/os_results.hpp>
|
||||||
#include <vapours/results/ncm_results.hpp>
|
#include <vapours/results/ncm_results.hpp>
|
||||||
|
#include <vapours/results/pgl_results.hpp>
|
||||||
#include <vapours/results/pm_results.hpp>
|
#include <vapours/results/pm_results.hpp>
|
||||||
#include <vapours/results/psc_results.hpp>
|
#include <vapours/results/psc_results.hpp>
|
||||||
#include <vapours/results/ro_results.hpp>
|
#include <vapours/results/ro_results.hpp>
|
||||||
|
|
30
libraries/libvapours/include/vapours/results/pgl_results.hpp
Normal file
30
libraries/libvapours/include/vapours/results/pgl_results.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours/results/results_common.hpp>
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
128
stratosphere/pgl/Makefile
Normal file
128
stratosphere/pgl/Makefile
Normal file
|
@ -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
|
||||||
|
#---------------------------------------------------------------------------------------
|
88
stratosphere/pgl/pgl.json
Normal file
88
stratosphere/pgl/pgl.json
Normal file
|
@ -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
|
||||||
|
}]
|
||||||
|
}
|
160
stratosphere/pgl/source/pgl_main.cpp
Normal file
160
stratosphere/pgl/source/pgl_main.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
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<NumServers> 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<pgl::srv::ShellInterface> GetSharedPointerToShellInterface() {
|
||||||
|
return ams::sf::ServiceObjectTraits<pgl::srv::ShellInterface>::SharedPointerHelper::GetEmptyDeleteSharedPointer(std::addressof(g_shell_interface));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterServiceSession() {
|
||||||
|
R_ABORT_UNLESS(g_server_manager.RegisterServer<pgl::srv::ShellInterface>(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;
|
||||||
|
}
|
||||||
|
|
|
@ -34,26 +34,6 @@ namespace ams::pm::impl {
|
||||||
u32 flags;
|
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<u32>(LaunchFlags_##flag) : static_cast<u32>(LaunchFlagsDeprecated_##flag))
|
#define GET_FLAG_MASK(flag) (hos_version >= hos::Version_5_0_0 ? static_cast<u32>(LaunchFlags_##flag) : static_cast<u32>(LaunchFlagsDeprecated_##flag))
|
||||||
|
|
||||||
inline bool ShouldSignalOnExit(u32 launch_flags) {
|
inline bool ShouldSignalOnExit(u32 launch_flags) {
|
||||||
|
|
Loading…
Reference in a new issue