mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-14 00:56:35 +00:00
pm: update to reflect 17.0.0 internal design changes
This commit is contained in:
parent
274f6b63f2
commit
0c3afff4d3
13 changed files with 428 additions and 332 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "ProcessManager",
|
"name": "ProcessManager",
|
||||||
"title_id": "0x0100000000000003",
|
"title_id": "0x0100000000000003",
|
||||||
"main_thread_stack_size": "0x00002000",
|
"main_thread_stack_size": "0x00003000",
|
||||||
"main_thread_priority": 49,
|
"main_thread_priority": 49,
|
||||||
"default_cpu_id": 3,
|
"default_cpu_id": 3,
|
||||||
"process_category": 1,
|
"process_category": 1,
|
||||||
|
|
|
@ -18,6 +18,64 @@
|
||||||
|
|
||||||
namespace ams::pm::impl {
|
namespace ams::pm::impl {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template<size_t MaxProcessInfos>
|
||||||
|
class ProcessInfoAllocator {
|
||||||
|
NON_COPYABLE(ProcessInfoAllocator);
|
||||||
|
NON_MOVEABLE(ProcessInfoAllocator);
|
||||||
|
static_assert(MaxProcessInfos >= 0x40, "MaxProcessInfos is too small.");
|
||||||
|
private:
|
||||||
|
util::TypedStorage<ProcessInfo> m_process_info_storages[MaxProcessInfos]{};
|
||||||
|
bool m_process_info_allocated[MaxProcessInfos]{};
|
||||||
|
os::SdkMutex m_lock{};
|
||||||
|
private:
|
||||||
|
constexpr inline size_t GetProcessInfoIndex(ProcessInfo *process_info) const {
|
||||||
|
return process_info - GetPointer(m_process_info_storages[0]);
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
constexpr ProcessInfoAllocator() = default;
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
ProcessInfo *AllocateProcessInfo(Args &&... args) {
|
||||||
|
std::scoped_lock lk(m_lock);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < MaxProcessInfos; i++) {
|
||||||
|
if (!m_process_info_allocated[i]) {
|
||||||
|
m_process_info_allocated[i] = true;
|
||||||
|
|
||||||
|
std::memset(m_process_info_storages + i, 0, sizeof(m_process_info_storages[i]));
|
||||||
|
|
||||||
|
return util::ConstructAt(m_process_info_storages[i], std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeProcessInfo(ProcessInfo *process_info) {
|
||||||
|
std::scoped_lock lk(m_lock);
|
||||||
|
|
||||||
|
const size_t index = this->GetProcessInfoIndex(process_info);
|
||||||
|
AMS_ABORT_UNLESS(index < MaxProcessInfos);
|
||||||
|
AMS_ABORT_UNLESS(m_process_info_allocated[index]);
|
||||||
|
|
||||||
|
util::DestroyAt(m_process_info_storages[index]);
|
||||||
|
m_process_info_allocated[index] = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Process lists. */
|
||||||
|
constinit ProcessList g_process_list;
|
||||||
|
constinit ProcessList g_exit_list;
|
||||||
|
|
||||||
|
/* Process Info Allocation. */
|
||||||
|
/* Note: The kernel slabheap is size 0x50 -- we allow slightly larger to account for the dead process list. */
|
||||||
|
constexpr size_t MaxProcessCount = 0x60;
|
||||||
|
constinit ProcessInfoAllocator<MaxProcessCount> g_process_info_allocator;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
ProcessInfo::ProcessInfo(os::NativeHandle h, os::ProcessId pid, ldr::PinId pin, const ncm::ProgramLocation &l, const cfg::OverrideStatus &s) : m_process_id(pid), m_pin_id(pin), m_loc(l), m_status(s), m_handle(h), m_state(svc::ProcessState_Created), m_flags(0) {
|
ProcessInfo::ProcessInfo(os::NativeHandle h, os::ProcessId pid, ldr::PinId pin, const ncm::ProgramLocation &l, const cfg::OverrideStatus &s) : m_process_id(pid), m_pin_id(pin), m_loc(l), m_status(s), m_handle(h), m_state(svc::ProcessState_Created), m_flags(0) {
|
||||||
os::InitializeMultiWaitHolder(std::addressof(m_multi_wait_holder), m_handle);
|
os::InitializeMultiWaitHolder(std::addressof(m_multi_wait_holder), m_handle);
|
||||||
os::SetMultiWaitHolderUserData(std::addressof(m_multi_wait_holder), reinterpret_cast<uintptr_t>(this));
|
os::SetMultiWaitHolderUserData(std::addressof(m_multi_wait_holder), reinterpret_cast<uintptr_t>(this));
|
||||||
|
@ -37,10 +95,27 @@ namespace ams::pm::impl {
|
||||||
/* Close the process's handle. */
|
/* Close the process's handle. */
|
||||||
os::CloseNativeHandle(m_handle);
|
os::CloseNativeHandle(m_handle);
|
||||||
m_handle = os::InvalidNativeHandle;
|
m_handle = os::InvalidNativeHandle;
|
||||||
|
|
||||||
/* Unlink the process from its multi wait. */
|
|
||||||
os::UnlinkMultiWaitHolder(std::addressof(m_multi_wait_holder));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProcessListAccessor GetProcessList() {
|
||||||
|
return ProcessListAccessor(g_process_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessListAccessor GetExitList() {
|
||||||
|
return ProcessListAccessor(g_exit_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessInfo *AllocateProcessInfo(svc::Handle process_handle, os::ProcessId process_id, ldr::PinId pin_id, const ncm::ProgramLocation &location, const cfg::OverrideStatus &override_status) {
|
||||||
|
return g_process_info_allocator.AllocateProcessInfo(process_handle, process_id, pin_id, location, override_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CleanupProcessInfo(ProcessListAccessor &list, ProcessInfo *process_info) {
|
||||||
|
/* Remove the process from the list. */
|
||||||
|
list->Remove(process_info);
|
||||||
|
|
||||||
|
/* Delete the process. */
|
||||||
|
g_process_info_allocator.FreeProcessInfo(process_info);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,8 +64,8 @@ namespace ams::pm::impl {
|
||||||
~ProcessInfo();
|
~ProcessInfo();
|
||||||
void Cleanup();
|
void Cleanup();
|
||||||
|
|
||||||
void LinkToMultiWait(os::MultiWaitType &multi_wait) {
|
os::MultiWaitHolderType *GetMultiWaitHolder() {
|
||||||
os::LinkMultiWaitHolder(std::addressof(multi_wait), std::addressof(m_multi_wait_holder));
|
return std::addressof(m_multi_wait_holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
os::NativeHandle GetHandle() const {
|
os::NativeHandle GetHandle() const {
|
||||||
|
@ -232,4 +232,10 @@ namespace ams::pm::impl {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ProcessListAccessor GetProcessList();
|
||||||
|
ProcessListAccessor GetExitList();
|
||||||
|
|
||||||
|
ProcessInfo *AllocateProcessInfo(svc::Handle process_handle, os::ProcessId process_id, ldr::PinId pin_id, const ncm::ProgramLocation &location, const cfg::OverrideStatus &override_status);
|
||||||
|
void CleanupProcessInfo(ProcessListAccessor &list, ProcessInfo *process_info);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "pm_process_manager.hpp"
|
#include "pm_process_manager.hpp"
|
||||||
#include "pm_resource_manager.hpp"
|
#include "pm_process_tracker.hpp"
|
||||||
|
|
||||||
#include "pm_process_info.hpp"
|
#include "pm_process_info.hpp"
|
||||||
|
#include "pm_spec.hpp"
|
||||||
|
|
||||||
namespace ams::pm::impl {
|
namespace ams::pm::impl {
|
||||||
|
|
||||||
|
@ -29,13 +29,7 @@ namespace ams::pm::impl {
|
||||||
HookType_Application = (1 << 1),
|
HookType_Application = (1 << 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LaunchProcessArgs {
|
#define GET_FLAG_MASK(flag) (hos_version >= hos::Version_5_0_0 ? static_cast<u32>(LaunchFlags_##flag) : static_cast<u32>(LaunchFlagsDeprecated_##flag))
|
||||||
os::ProcessId *out_process_id;
|
|
||||||
ncm::ProgramLocation location;
|
|
||||||
u32 flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
#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) {
|
||||||
const auto hos_version = hos::GetVersion();
|
const auto hos_version = hos::GetVersion();
|
||||||
|
@ -70,115 +64,26 @@ namespace ams::pm::impl {
|
||||||
return launch_flags & GET_FLAG_MASK(DisableAslr);
|
return launch_flags & GET_FLAG_MASK(DisableAslr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef GET_FLAG_MASK
|
#undef GET_FLAG_MASK
|
||||||
|
|
||||||
template<size_t MaxProcessInfos>
|
|
||||||
class ProcessInfoAllocator {
|
|
||||||
NON_COPYABLE(ProcessInfoAllocator);
|
|
||||||
NON_MOVEABLE(ProcessInfoAllocator);
|
|
||||||
static_assert(MaxProcessInfos >= 0x40, "MaxProcessInfos is too small.");
|
|
||||||
private:
|
|
||||||
util::TypedStorage<ProcessInfo> m_process_info_storages[MaxProcessInfos]{};
|
|
||||||
bool m_process_info_allocated[MaxProcessInfos]{};
|
|
||||||
os::SdkMutex m_lock{};
|
|
||||||
private:
|
|
||||||
constexpr inline size_t GetProcessInfoIndex(ProcessInfo *process_info) const {
|
|
||||||
return process_info - GetPointer(m_process_info_storages[0]);
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
constexpr ProcessInfoAllocator() = default;
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
ProcessInfo *AllocateProcessInfo(Args &&... args) {
|
|
||||||
std::scoped_lock lk(m_lock);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < MaxProcessInfos; i++) {
|
|
||||||
if (!m_process_info_allocated[i]) {
|
|
||||||
m_process_info_allocated[i] = true;
|
|
||||||
|
|
||||||
std::memset(m_process_info_storages + i, 0, sizeof(m_process_info_storages[i]));
|
|
||||||
|
|
||||||
return util::ConstructAt(m_process_info_storages[i], std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeProcessInfo(ProcessInfo *process_info) {
|
|
||||||
std::scoped_lock lk(m_lock);
|
|
||||||
|
|
||||||
const size_t index = this->GetProcessInfoIndex(process_info);
|
|
||||||
AMS_ABORT_UNLESS(index < MaxProcessInfos);
|
|
||||||
AMS_ABORT_UNLESS(m_process_info_allocated[index]);
|
|
||||||
|
|
||||||
util::DestroyAt(m_process_info_storages[index]);
|
|
||||||
m_process_info_allocated[index] = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Process Tracking globals. */
|
/* Process Tracking globals. */
|
||||||
void ProcessTrackingMain(void *);
|
constinit ProcessTracker g_process_tracker;
|
||||||
|
alignas(os::ThreadStackAlignment) constinit u8 g_process_track_thread_stack[8_KB];
|
||||||
constinit os::ThreadType g_process_track_thread;
|
|
||||||
alignas(os::ThreadStackAlignment) constinit u8 g_process_track_thread_stack[16_KB];
|
|
||||||
|
|
||||||
/* Process lists. */
|
|
||||||
constinit ProcessList g_process_list;
|
|
||||||
constinit ProcessList g_dead_process_list;
|
|
||||||
|
|
||||||
/* Process Info Allocation. */
|
|
||||||
/* Note: The kernel slabheap is size 0x50 -- we allow slightly larger to account for the dead process list. */
|
|
||||||
constexpr size_t MaxProcessCount = 0x60;
|
|
||||||
constinit ProcessInfoAllocator<MaxProcessCount> g_process_info_allocator;
|
|
||||||
|
|
||||||
/* Global events. */
|
/* Global events. */
|
||||||
constinit os::SystemEventType g_process_event;
|
|
||||||
constinit os::SystemEventType g_hook_to_create_process_event;
|
constinit os::SystemEventType g_hook_to_create_process_event;
|
||||||
constinit os::SystemEventType g_hook_to_create_application_process_event;
|
constinit os::SystemEventType g_hook_to_create_application_process_event;
|
||||||
constinit os::SystemEventType g_boot_finished_event;
|
constinit os::SystemEventType g_boot_finished_event;
|
||||||
|
|
||||||
/* Process Launch synchronization globals. */
|
|
||||||
constinit os::SdkMutex g_launch_program_lock;
|
|
||||||
os::Event g_process_launch_start_event(os::EventClearMode_AutoClear);
|
|
||||||
os::Event g_process_launch_finish_event(os::EventClearMode_AutoClear);
|
|
||||||
constinit Result g_process_launch_result = ResultSuccess();
|
|
||||||
constinit LaunchProcessArgs g_process_launch_args = {};
|
|
||||||
|
|
||||||
/* Hook globals. */
|
/* Hook globals. */
|
||||||
constinit std::atomic<ncm::ProgramId> g_program_id_hook;
|
constinit std::atomic<ncm::ProgramId> g_program_id_hook;
|
||||||
constinit std::atomic<bool> g_application_hook;
|
constinit std::atomic<bool> g_application_hook;
|
||||||
|
|
||||||
/* Forward declarations. */
|
|
||||||
Result LaunchProcess(os::MultiWaitType &multi_wait, const LaunchProcessArgs &args);
|
|
||||||
void OnProcessSignaled(ProcessListAccessor &list, ProcessInfo *process_info);
|
|
||||||
|
|
||||||
/* Helpers. */
|
/* Helpers. */
|
||||||
void ProcessTrackingMain(void *) {
|
void CreateDebuggerEvent() {
|
||||||
/* This is the main loop of the process tracking thread. */
|
/* Create debugger hook events. */
|
||||||
|
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_process_event), os::EventClearMode_AutoClear, true));
|
||||||
/* Setup multi wait/holders. */
|
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_application_process_event), os::EventClearMode_AutoClear, true));
|
||||||
os::MultiWaitType process_multi_wait;
|
|
||||||
os::MultiWaitHolderType start_event_holder;
|
|
||||||
os::InitializeMultiWait(std::addressof(process_multi_wait));
|
|
||||||
os::InitializeMultiWaitHolder(std::addressof(start_event_holder), g_process_launch_start_event.GetBase());
|
|
||||||
os::LinkMultiWaitHolder(std::addressof(process_multi_wait), std::addressof(start_event_holder));
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
auto signaled_holder = os::WaitAny(std::addressof(process_multi_wait));
|
|
||||||
if (signaled_holder == std::addressof(start_event_holder)) {
|
|
||||||
/* Launch start event signaled. */
|
|
||||||
/* TryWait will clear signaled, preventing duplicate notifications. */
|
|
||||||
if (g_process_launch_start_event.TryWait()) {
|
|
||||||
g_process_launch_result = LaunchProcess(process_multi_wait, g_process_launch_args);
|
|
||||||
g_process_launch_finish_event.Signal();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Some process was signaled. */
|
|
||||||
ProcessListAccessor list(g_process_list);
|
|
||||||
OnProcessSignaled(list, reinterpret_cast<ProcessInfo *>(os::GetMultiWaitHolderUserData(signaled_holder)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline u32 GetLoaderCreateProcessFlags(u32 launch_flags) {
|
inline u32 GetLoaderCreateProcessFlags(u32 launch_flags) {
|
||||||
|
@ -195,7 +100,7 @@ namespace ams::pm::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HasApplicationProcess() {
|
bool HasApplicationProcess() {
|
||||||
ProcessListAccessor list(g_process_list);
|
auto list = GetProcessList();
|
||||||
|
|
||||||
for (auto &process : *list) {
|
for (auto &process : *list) {
|
||||||
if (process.IsApplication()) {
|
if (process.IsApplication()) {
|
||||||
|
@ -212,19 +117,14 @@ namespace ams::pm::impl {
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CleanupProcessInfo(ProcessListAccessor &list, ProcessInfo *process_info) {
|
Result LaunchProgramImpl(ProcessInfo **out_process_info, os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 flags) {
|
||||||
/* Remove the process from the list. */
|
/* Set the output to nullptr, if we fail. */
|
||||||
list->Remove(process_info);
|
*out_process_info = nullptr;
|
||||||
|
|
||||||
/* Delete the process. */
|
|
||||||
g_process_info_allocator.FreeProcessInfo(process_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result LaunchProcess(os::MultiWaitType &multi_wait, const LaunchProcessArgs &args) {
|
|
||||||
/* Get Program Info. */
|
/* Get Program Info. */
|
||||||
ldr::ProgramInfo program_info;
|
ldr::ProgramInfo program_info;
|
||||||
cfg::OverrideStatus override_status;
|
cfg::OverrideStatus override_status;
|
||||||
R_TRY(ldr::pm::AtmosphereGetProgramInfo(std::addressof(program_info), std::addressof(override_status), args.location));
|
R_TRY(ldr::pm::AtmosphereGetProgramInfo(std::addressof(program_info), std::addressof(override_status), loc));
|
||||||
const bool is_application = (program_info.flags & ldr::ProgramInfoFlag_ApplicationTypeMask) == ldr::ProgramInfoFlag_Application;
|
const bool is_application = (program_info.flags & ldr::ProgramInfoFlag_ApplicationTypeMask) == ldr::ProgramInfoFlag_Application;
|
||||||
const bool allow_debug = (program_info.flags & ldr::ProgramInfoFlag_AllowDebug) || hos::GetVersion() < hos::Version_2_0_0;
|
const bool allow_debug = (program_info.flags & ldr::ProgramInfoFlag_AllowDebug) || hos::GetVersion() < hos::Version_2_0_0;
|
||||||
|
|
||||||
|
@ -232,14 +132,14 @@ namespace ams::pm::impl {
|
||||||
R_UNLESS(!is_application || !HasApplicationProcess(), pm::ResultApplicationRunning());
|
R_UNLESS(!is_application || !HasApplicationProcess(), pm::ResultApplicationRunning());
|
||||||
|
|
||||||
/* Fix the program location to use the right program id. */
|
/* Fix the program location to use the right program id. */
|
||||||
const ncm::ProgramLocation location = ncm::ProgramLocation::Make(program_info.program_id, static_cast<ncm::StorageId>(args.location.storage_id));
|
const ncm::ProgramLocation fixed_location = ncm::ProgramLocation::Make(program_info.program_id, static_cast<ncm::StorageId>(loc.storage_id));
|
||||||
|
|
||||||
/* Pin and create the process. */
|
/* Pin and create the process. */
|
||||||
os::NativeHandle process_handle;
|
os::NativeHandle process_handle;
|
||||||
ldr::PinId pin_id;
|
ldr::PinId pin_id;
|
||||||
{
|
{
|
||||||
/* Pin the program with loader. */
|
/* Pin the program with loader. */
|
||||||
R_TRY(ldr::pm::AtmospherePinProgram(std::addressof(pin_id), location, override_status));
|
R_TRY(ldr::pm::AtmospherePinProgram(std::addressof(pin_id), fixed_location, override_status));
|
||||||
|
|
||||||
/* If we fail after now, unpin. */
|
/* If we fail after now, unpin. */
|
||||||
ON_RESULT_FAILURE { ldr::pm::UnpinProgram(pin_id); };
|
ON_RESULT_FAILURE { ldr::pm::UnpinProgram(pin_id); };
|
||||||
|
@ -263,29 +163,28 @@ namespace ams::pm::impl {
|
||||||
ON_RESULT_FAILURE_2 { if (mitm_boost_size > 0 || is_application) { R_ABORT_UNLESS(BoostSystemMemoryResourceLimitForMitm(0)); } };
|
ON_RESULT_FAILURE_2 { if (mitm_boost_size > 0 || is_application) { R_ABORT_UNLESS(BoostSystemMemoryResourceLimitForMitm(0)); } };
|
||||||
|
|
||||||
/* Ensure resources are available. */
|
/* Ensure resources are available. */
|
||||||
resource::WaitResourceAvailable(std::addressof(program_info));
|
WaitResourceAvailable(std::addressof(program_info));
|
||||||
|
|
||||||
/* Actually create the process. */
|
/* Actually create the process. */
|
||||||
R_TRY(ldr::pm::CreateProcess(std::addressof(process_handle), pin_id, GetLoaderCreateProcessFlags(args.flags), resource::GetResourceLimitHandle(std::addressof(program_info))));
|
R_TRY(ldr::pm::CreateProcess(std::addressof(process_handle), pin_id, GetLoaderCreateProcessFlags(flags), GetResourceLimitHandle(std::addressof(program_info))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the process id. */
|
/* Get the process id. */
|
||||||
os::ProcessId process_id = os::GetProcessId(process_handle);
|
os::ProcessId process_id = os::GetProcessId(process_handle);
|
||||||
|
|
||||||
/* Make new process info. */
|
/* Make new process info. */
|
||||||
ProcessInfo *process_info = g_process_info_allocator.AllocateProcessInfo(process_handle, process_id, pin_id, location, override_status);
|
ProcessInfo *process_info = AllocateProcessInfo(process_handle, process_id, pin_id, fixed_location, override_status);
|
||||||
AMS_ABORT_UNLESS(process_info != nullptr);
|
AMS_ABORT_UNLESS(process_info != nullptr);
|
||||||
|
|
||||||
/* Link new process info. */
|
/* Add the new process info to the process list. */
|
||||||
{
|
{
|
||||||
ProcessListAccessor list(g_process_list);
|
auto list = GetProcessList();
|
||||||
list->push_back(*process_info);
|
list->push_back(*process_info);
|
||||||
process_info->LinkToMultiWait(multi_wait);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prevent resource leakage if register fails. */
|
/* Prevent resource leakage if register fails. */
|
||||||
ON_RESULT_FAILURE {
|
ON_RESULT_FAILURE {
|
||||||
ProcessListAccessor list(g_process_list);
|
auto list = GetProcessList();
|
||||||
process_info->Cleanup();
|
process_info->Cleanup();
|
||||||
CleanupProcessInfo(list, process_info);
|
CleanupProcessInfo(list, process_info);
|
||||||
};
|
};
|
||||||
|
@ -296,166 +195,73 @@ namespace ams::pm::impl {
|
||||||
const u8 *aci_fah = acid_fac + program_info.acid_fac_size;
|
const u8 *aci_fah = acid_fac + program_info.acid_fac_size;
|
||||||
|
|
||||||
/* Register with FS and SM. */
|
/* Register with FS and SM. */
|
||||||
R_TRY(fsprRegisterProgram(static_cast<u64>(process_id), static_cast<u64>(location.program_id), static_cast<NcmStorageId>(location.storage_id), aci_fah, program_info.aci_fah_size, acid_fac, program_info.acid_fac_size));
|
R_TRY(fsprRegisterProgram(static_cast<u64>(process_id), static_cast<u64>(fixed_location.program_id), static_cast<NcmStorageId>(fixed_location.storage_id), aci_fah, program_info.aci_fah_size, acid_fac, program_info.acid_fac_size));
|
||||||
R_TRY(sm::manager::RegisterProcess(process_id, location.program_id, override_status, acid_sac, program_info.acid_sac_size, aci_sac, program_info.aci_sac_size));
|
R_TRY(sm::manager::RegisterProcess(process_id, fixed_location.program_id, override_status, acid_sac, program_info.acid_sac_size, aci_sac, program_info.aci_sac_size));
|
||||||
|
|
||||||
/* Set flags. */
|
/* Set flags. */
|
||||||
if (is_application) {
|
if (is_application) {
|
||||||
process_info->SetApplication();
|
process_info->SetApplication();
|
||||||
}
|
}
|
||||||
if (ShouldSignalOnStart(args.flags) && allow_debug) {
|
if (ShouldSignalOnStart(flags) && allow_debug) {
|
||||||
process_info->SetSignalOnStart();
|
process_info->SetSignalOnStart();
|
||||||
}
|
}
|
||||||
if (ShouldSignalOnExit(args.flags)) {
|
if (ShouldSignalOnExit(flags)) {
|
||||||
process_info->SetSignalOnExit();
|
process_info->SetSignalOnExit();
|
||||||
}
|
}
|
||||||
if (ShouldSignalOnDebugEvent(args.flags) && allow_debug) {
|
if (ShouldSignalOnDebugEvent(flags) && allow_debug) {
|
||||||
process_info->SetSignalOnDebugEvent();
|
process_info->SetSignalOnDebugEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process hooks/signaling. */
|
/* Process hooks/signaling. */
|
||||||
if (location.program_id == g_program_id_hook) {
|
if (fixed_location.program_id == g_program_id_hook) {
|
||||||
os::SignalSystemEvent(std::addressof(g_hook_to_create_process_event));
|
os::SignalSystemEvent(std::addressof(g_hook_to_create_process_event));
|
||||||
g_program_id_hook = ncm::InvalidProgramId;
|
g_program_id_hook = ncm::InvalidProgramId;
|
||||||
} else if (is_application && g_application_hook) {
|
} else if (is_application && g_application_hook) {
|
||||||
os::SignalSystemEvent(std::addressof(g_hook_to_create_application_process_event));
|
os::SignalSystemEvent(std::addressof(g_hook_to_create_application_process_event));
|
||||||
g_application_hook = false;
|
g_application_hook = false;
|
||||||
} else if (!ShouldStartSuspended(args.flags)) {
|
} else if (!ShouldStartSuspended(flags)) {
|
||||||
R_TRY(StartProcess(process_info, std::addressof(program_info)));
|
R_TRY(StartProcess(process_info, std::addressof(program_info)));
|
||||||
}
|
}
|
||||||
|
|
||||||
*args.out_process_id = process_id;
|
*out_process_id = process_id;
|
||||||
|
*out_process_info = process_info;
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnProcessSignaled(ProcessListAccessor &list, ProcessInfo *process_info) {
|
|
||||||
/* Reset the process's signal. */
|
|
||||||
svc::ResetSignal(process_info->GetHandle());
|
|
||||||
|
|
||||||
/* Update the process's state. */
|
|
||||||
const svc::ProcessState old_state = process_info->GetState();
|
|
||||||
{
|
|
||||||
s64 tmp = 0;
|
|
||||||
R_ABORT_UNLESS(svc::GetProcessInfo(std::addressof(tmp), process_info->GetHandle(), svc::ProcessInfoType_ProcessState));
|
|
||||||
process_info->SetState(static_cast<svc::ProcessState>(tmp));
|
|
||||||
}
|
|
||||||
const svc::ProcessState new_state = process_info->GetState();
|
|
||||||
|
|
||||||
/* If we're transitioning away from crashed, clear waiting attached. */
|
|
||||||
if (old_state == svc::ProcessState_Crashed && new_state != svc::ProcessState_Crashed) {
|
|
||||||
process_info->ClearExceptionWaitingAttach();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (new_state) {
|
|
||||||
case svc::ProcessState_Created:
|
|
||||||
case svc::ProcessState_CreatedAttached:
|
|
||||||
case svc::ProcessState_Terminating:
|
|
||||||
break;
|
|
||||||
case svc::ProcessState_Running:
|
|
||||||
if (process_info->ShouldSignalOnDebugEvent()) {
|
|
||||||
process_info->ClearSuspended();
|
|
||||||
process_info->SetSuspendedStateChanged();
|
|
||||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
|
||||||
} else if (hos::GetVersion() >= hos::Version_2_0_0 && process_info->ShouldSignalOnStart()) {
|
|
||||||
process_info->SetStartedStateChanged();
|
|
||||||
process_info->ClearSignalOnStart();
|
|
||||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
|
||||||
}
|
|
||||||
process_info->ClearUnhandledException();
|
|
||||||
break;
|
|
||||||
case svc::ProcessState_Crashed:
|
|
||||||
if (!process_info->HasUnhandledException()) {
|
|
||||||
process_info->SetExceptionOccurred();
|
|
||||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
|
||||||
}
|
|
||||||
process_info->SetExceptionWaitingAttach();
|
|
||||||
break;
|
|
||||||
case svc::ProcessState_RunningAttached:
|
|
||||||
if (process_info->ShouldSignalOnDebugEvent()) {
|
|
||||||
process_info->ClearSuspended();
|
|
||||||
process_info->SetSuspendedStateChanged();
|
|
||||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
|
||||||
}
|
|
||||||
process_info->ClearUnhandledException();
|
|
||||||
break;
|
|
||||||
case svc::ProcessState_Terminated:
|
|
||||||
/* Free process resources, unlink from multi wait. */
|
|
||||||
process_info->Cleanup();
|
|
||||||
|
|
||||||
if (hos::GetVersion() < hos::Version_5_0_0 && process_info->ShouldSignalOnExit()) {
|
|
||||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
|
||||||
} else {
|
|
||||||
/* Handle the case where we need to keep the process alive some time longer. */
|
|
||||||
if (hos::GetVersion() >= hos::Version_5_0_0 && process_info->ShouldSignalOnExit()) {
|
|
||||||
/* Remove from the living list. */
|
|
||||||
list->Remove(process_info);
|
|
||||||
|
|
||||||
/* Add the process to the list of dead processes. */
|
|
||||||
{
|
|
||||||
ProcessListAccessor dead_list(g_dead_process_list);
|
|
||||||
dead_list->push_back(*process_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Signal. */
|
|
||||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
|
||||||
} else {
|
|
||||||
/* Actually delete process. */
|
|
||||||
CleanupProcessInfo(list, process_info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case svc::ProcessState_DebugBreak:
|
|
||||||
if (process_info->ShouldSignalOnDebugEvent()) {
|
|
||||||
process_info->SetSuspended();
|
|
||||||
process_info->SetSuspendedStateChanged();
|
|
||||||
os::SignalSystemEvent(std::addressof(g_process_event));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialization. */
|
/* Initialization. */
|
||||||
Result InitializeProcessManager() {
|
Result InitializeProcessManager() {
|
||||||
/* Create events. */
|
/* Create events. */
|
||||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_process_event), os::EventClearMode_AutoClear, true));
|
CreateProcessEvent();
|
||||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_process_event), os::EventClearMode_AutoClear, true));
|
CreateDebuggerEvent();
|
||||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_application_process_event), os::EventClearMode_AutoClear, true));
|
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_boot_finished_event), os::EventClearMode_AutoClear, true));
|
||||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_boot_finished_event), os::EventClearMode_AutoClear, true));
|
|
||||||
|
|
||||||
/* Initialize resource limits. */
|
/* Initialize resource limits. */
|
||||||
R_TRY(resource::InitializeResourceManager());
|
R_TRY(InitializeSpec());
|
||||||
|
|
||||||
/* Create thread. */
|
/* Initialize the process tracker. */
|
||||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_process_track_thread), ProcessTrackingMain, nullptr, g_process_track_thread_stack, sizeof(g_process_track_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(pm, ProcessTrack)));
|
g_process_tracker.Initialize(g_process_track_thread_stack, sizeof(g_process_track_thread_stack));
|
||||||
os::SetThreadNamePointer(std::addressof(g_process_track_thread), AMS_GET_SYSTEM_THREAD_NAME(pm, ProcessTrack));
|
|
||||||
|
|
||||||
/* Start thread. */
|
/* Start the process tracker thread. */
|
||||||
os::StartThread(std::addressof(g_process_track_thread));
|
g_process_tracker.StartThread();
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process Management. */
|
/* Process Management. */
|
||||||
Result LaunchProgram(os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 flags) {
|
Result LaunchProgram(os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 flags) {
|
||||||
/* Ensure we only try to launch one program at a time. */
|
/* Launch the program. */
|
||||||
std::scoped_lock lk(g_launch_program_lock);
|
ProcessInfo *process_info = nullptr;
|
||||||
|
R_TRY(LaunchProgramImpl(std::addressof(process_info), out_process_id, loc, flags));
|
||||||
|
|
||||||
/* Set global arguments, signal, wait. */
|
/* Register the process info with the tracker. */
|
||||||
g_process_launch_args = {
|
g_process_tracker.QueueEntry(process_info);
|
||||||
.out_process_id = out_process_id,
|
R_SUCCEED();
|
||||||
.location = loc,
|
|
||||||
.flags = flags,
|
|
||||||
};
|
|
||||||
g_process_launch_start_event.Signal();
|
|
||||||
g_process_launch_finish_event.Wait();
|
|
||||||
|
|
||||||
R_RETURN(g_process_launch_result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result StartProcess(os::ProcessId process_id) {
|
Result StartProcess(os::ProcessId process_id) {
|
||||||
ProcessListAccessor list(g_process_list);
|
auto list = GetProcessList();
|
||||||
|
|
||||||
auto process_info = list->Find(process_id);
|
auto process_info = list->Find(process_id);
|
||||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||||
|
@ -467,7 +273,7 @@ namespace ams::pm::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result TerminateProcess(os::ProcessId process_id) {
|
Result TerminateProcess(os::ProcessId process_id) {
|
||||||
ProcessListAccessor list(g_process_list);
|
auto list = GetProcessList();
|
||||||
|
|
||||||
auto process_info = list->Find(process_id);
|
auto process_info = list->Find(process_id);
|
||||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||||
|
@ -476,7 +282,7 @@ namespace ams::pm::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result TerminateProgram(ncm::ProgramId program_id) {
|
Result TerminateProgram(ncm::ProgramId program_id) {
|
||||||
ProcessListAccessor list(g_process_list);
|
auto list = GetProcessList();
|
||||||
|
|
||||||
auto process_info = list->Find(program_id);
|
auto process_info = list->Find(program_id);
|
||||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||||
|
@ -484,15 +290,10 @@ namespace ams::pm::impl {
|
||||||
R_RETURN(svc::TerminateProcess(process_info->GetHandle()));
|
R_RETURN(svc::TerminateProcess(process_info->GetHandle()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetProcessEventHandle(os::NativeHandle *out) {
|
|
||||||
*out = os::GetReadableHandleOfSystemEvent(std::addressof(g_process_event));
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GetProcessEventInfo(ProcessEventInfo *out) {
|
Result GetProcessEventInfo(ProcessEventInfo *out) {
|
||||||
/* Check for event from current process. */
|
/* Check for event from current process. */
|
||||||
{
|
{
|
||||||
ProcessListAccessor list(g_process_list);
|
auto list = GetProcessList();
|
||||||
|
|
||||||
|
|
||||||
for (auto &process : *list) {
|
for (auto &process : *list) {
|
||||||
|
@ -528,14 +329,14 @@ namespace ams::pm::impl {
|
||||||
|
|
||||||
/* Check for event from exited process. */
|
/* Check for event from exited process. */
|
||||||
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
||||||
ProcessListAccessor dead_list(g_dead_process_list);
|
auto exit_list = GetExitList();
|
||||||
|
|
||||||
if (!dead_list->empty()) {
|
if (!exit_list->empty()) {
|
||||||
auto &process_info = dead_list->front();
|
auto &process_info = exit_list->front();
|
||||||
out->event = GetProcessEventValue(ProcessEvent::Exited);
|
out->event = GetProcessEventValue(ProcessEvent::Exited);
|
||||||
out->process_id = process_info.GetProcessId();
|
out->process_id = process_info.GetProcessId();
|
||||||
|
|
||||||
CleanupProcessInfo(dead_list, std::addressof(process_info));
|
CleanupProcessInfo(exit_list, std::addressof(process_info));
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -546,7 +347,7 @@ namespace ams::pm::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result CleanupProcess(os::ProcessId process_id) {
|
Result CleanupProcess(os::ProcessId process_id) {
|
||||||
ProcessListAccessor list(g_process_list);
|
auto list = GetProcessList();
|
||||||
|
|
||||||
auto process_info = list->Find(process_id);
|
auto process_info = list->Find(process_id);
|
||||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||||
|
@ -557,7 +358,7 @@ namespace ams::pm::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ClearExceptionOccurred(os::ProcessId process_id) {
|
Result ClearExceptionOccurred(os::ProcessId process_id) {
|
||||||
ProcessListAccessor list(g_process_list);
|
auto list = GetProcessList();
|
||||||
|
|
||||||
auto process_info = list->Find(process_id);
|
auto process_info = list->Find(process_id);
|
||||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||||
|
@ -575,7 +376,7 @@ namespace ams::pm::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetExceptionProcessIdList(u32 *out_count, os::ProcessId *out_process_ids, size_t max_out_count) {
|
Result GetExceptionProcessIdList(u32 *out_count, os::ProcessId *out_process_ids, size_t max_out_count) {
|
||||||
ProcessListAccessor list(g_process_list);
|
auto list = GetProcessList();
|
||||||
|
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
|
|
||||||
|
@ -596,7 +397,7 @@ namespace ams::pm::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetProcessId(os::ProcessId *out, ncm::ProgramId program_id) {
|
Result GetProcessId(os::ProcessId *out, ncm::ProgramId program_id) {
|
||||||
ProcessListAccessor list(g_process_list);
|
auto list = GetProcessList();
|
||||||
|
|
||||||
auto process_info = list->Find(program_id);
|
auto process_info = list->Find(program_id);
|
||||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||||
|
@ -606,7 +407,7 @@ namespace ams::pm::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetProgramId(ncm::ProgramId *out, os::ProcessId process_id) {
|
Result GetProgramId(ncm::ProgramId *out, os::ProcessId process_id) {
|
||||||
ProcessListAccessor list(g_process_list);
|
auto list = GetProcessList();
|
||||||
|
|
||||||
auto process_info = list->Find(process_id);
|
auto process_info = list->Find(process_id);
|
||||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||||
|
@ -616,7 +417,7 @@ namespace ams::pm::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetApplicationProcessId(os::ProcessId *out_process_id) {
|
Result GetApplicationProcessId(os::ProcessId *out_process_id) {
|
||||||
ProcessListAccessor list(g_process_list);
|
auto list = GetProcessList();
|
||||||
|
|
||||||
for (auto &process : *list) {
|
for (auto &process : *list) {
|
||||||
if (process.IsApplication()) {
|
if (process.IsApplication()) {
|
||||||
|
@ -629,7 +430,7 @@ namespace ams::pm::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AtmosphereGetProcessInfo(os::NativeHandle *out_process_handle, ncm::ProgramLocation *out_loc, cfg::OverrideStatus *out_status, os::ProcessId process_id) {
|
Result AtmosphereGetProcessInfo(os::NativeHandle *out_process_handle, ncm::ProgramLocation *out_loc, cfg::OverrideStatus *out_status, os::ProcessId process_id) {
|
||||||
ProcessListAccessor list(g_process_list);
|
auto list = GetProcessList();
|
||||||
|
|
||||||
auto process_info = list->Find(process_id);
|
auto process_info = list->Find(process_id);
|
||||||
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
|
||||||
|
@ -706,33 +507,4 @@ namespace ams::pm::impl {
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resource Limit API. */
|
|
||||||
Result BoostSystemMemoryResourceLimit(u64 boost_size) {
|
|
||||||
R_RETURN(resource::BoostSystemMemoryResourceLimit(boost_size));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result BoostApplicationThreadResourceLimit() {
|
|
||||||
R_RETURN(resource::BoostApplicationThreadResourceLimit());
|
|
||||||
}
|
|
||||||
|
|
||||||
Result BoostSystemThreadResourceLimit() {
|
|
||||||
R_RETURN(resource::BoostSystemThreadResourceLimit());
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GetAppletResourceLimitCurrentValue(pm::ResourceLimitValue *out) {
|
|
||||||
R_RETURN(resource::GetResourceLimitCurrentValue(ResourceLimitGroup_Applet, out));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GetAppletResourceLimitPeakValue(pm::ResourceLimitValue *out) {
|
|
||||||
R_RETURN(resource::GetResourceLimitPeakValue(ResourceLimitGroup_Applet, out));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result AtmosphereGetCurrentLimitInfo(s64 *out_cur_val, s64 *out_lim_val, u32 group, u32 resource) {
|
|
||||||
R_RETURN(resource::GetResourceLimitValues(out_cur_val, out_lim_val, static_cast<ResourceLimitGroup>(group), static_cast<svc::LimitableResource>(resource)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size) {
|
|
||||||
R_RETURN(resource::BoostSystemMemoryResourceLimitForMitm(boost_size));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,13 +48,4 @@ namespace ams::pm::impl {
|
||||||
Result NotifyBootFinished();
|
Result NotifyBootFinished();
|
||||||
Result GetBootFinishedEventHandle(os::NativeHandle *out);
|
Result GetBootFinishedEventHandle(os::NativeHandle *out);
|
||||||
|
|
||||||
/* Resource Limit API. */
|
|
||||||
Result BoostSystemMemoryResourceLimit(u64 boost_size);
|
|
||||||
Result BoostApplicationThreadResourceLimit();
|
|
||||||
Result BoostSystemThreadResourceLimit();
|
|
||||||
Result GetAppletResourceLimitCurrentValue(pm::ResourceLimitValue *out);
|
|
||||||
Result GetAppletResourceLimitPeakValue(pm::ResourceLimitValue *out);
|
|
||||||
Result AtmosphereGetCurrentLimitInfo(s64 *out_cur_val, s64 *out_lim_val, u32 group, u32 resource);
|
|
||||||
Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
193
stratosphere/pm/source/impl/pm_process_tracker.cpp
Normal file
193
stratosphere/pm/source/impl/pm_process_tracker.cpp
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "pm_process_tracker.hpp"
|
||||||
|
#include "pm_process_info.hpp"
|
||||||
|
#include "pm_spec.hpp"
|
||||||
|
|
||||||
|
namespace ams::pm::impl {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/* Global process event. */
|
||||||
|
constinit os::SystemEventType g_process_event;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessTracker::Initialize(void *stack, size_t stack_size) {
|
||||||
|
/* Initialize our events. */
|
||||||
|
os::InitializeEvent(std::addressof(m_request_event), false, os::EventClearMode_AutoClear);
|
||||||
|
os::InitializeEvent(std::addressof(m_reply_event), false, os::EventClearMode_AutoClear);
|
||||||
|
|
||||||
|
/* Our process count should initially be zero. */
|
||||||
|
m_process_count = 0;
|
||||||
|
|
||||||
|
/* Create the process tracking thread. */
|
||||||
|
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_thread), ProcessTracker::ThreadFunction, this, stack, stack_size, AMS_GET_SYSTEM_THREAD_PRIORITY(pm, ProcessTrack)));
|
||||||
|
os::SetThreadNamePointer(std::addressof(m_thread), AMS_GET_SYSTEM_THREAD_NAME(pm, ProcessTrack));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessTracker::StartThread() {
|
||||||
|
/* Start thread. */
|
||||||
|
os::StartThread(std::addressof(m_thread));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessTracker::ThreadBody() {
|
||||||
|
/* This is the main loop of the process tracking thread. */
|
||||||
|
|
||||||
|
/* Setup multi wait/holders. */
|
||||||
|
os::MultiWaitType process_multi_wait;
|
||||||
|
os::MultiWaitHolderType enqueue_event_holder;
|
||||||
|
os::InitializeMultiWait(std::addressof(process_multi_wait));
|
||||||
|
os::InitializeMultiWaitHolder(std::addressof(enqueue_event_holder), std::addressof(m_request_event));
|
||||||
|
os::LinkMultiWaitHolder(std::addressof(process_multi_wait), std::addressof(enqueue_event_holder));
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
auto signaled_holder = os::WaitAny(std::addressof(process_multi_wait));
|
||||||
|
if (signaled_holder == std::addressof(enqueue_event_holder)) {
|
||||||
|
/* TryWait will clear signaled, preventing duplicate notifications. */
|
||||||
|
if (os::TryWaitEvent(std::addressof(m_request_event))) {
|
||||||
|
/* Link the process to our multi-wait. */
|
||||||
|
os::LinkMultiWaitHolder(std::addressof(process_multi_wait), m_queued_process_info->GetMultiWaitHolder());
|
||||||
|
m_queued_process_info = nullptr;
|
||||||
|
|
||||||
|
/* Increment our process count. */
|
||||||
|
++m_process_count;
|
||||||
|
|
||||||
|
/* Reply. */
|
||||||
|
os::SignalEvent(std::addressof(m_reply_event));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Some process was signaled. */
|
||||||
|
this->OnProcessSignaled(reinterpret_cast<ProcessInfo *>(os::GetMultiWaitHolderUserData(signaled_holder)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessTracker::QueueEntry(ProcessInfo *process_info) {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
/* Request to enqueue the process info. */
|
||||||
|
m_queued_process_info = process_info;
|
||||||
|
os::SignalEvent(std::addressof(m_request_event));
|
||||||
|
|
||||||
|
/* Wait for acknowledgement. */
|
||||||
|
os::WaitEvent(std::addressof(m_reply_event));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessTracker::OnProcessSignaled(ProcessInfo *process_info) {
|
||||||
|
/* Get the process list. */
|
||||||
|
auto list = GetProcessList();
|
||||||
|
|
||||||
|
/* Reset the process's signal. */
|
||||||
|
svc::ResetSignal(process_info->GetHandle());
|
||||||
|
|
||||||
|
/* Update the process's state. */
|
||||||
|
const svc::ProcessState old_state = process_info->GetState();
|
||||||
|
{
|
||||||
|
s64 tmp = 0;
|
||||||
|
R_ABORT_UNLESS(svc::GetProcessInfo(std::addressof(tmp), process_info->GetHandle(), svc::ProcessInfoType_ProcessState));
|
||||||
|
process_info->SetState(static_cast<svc::ProcessState>(tmp));
|
||||||
|
}
|
||||||
|
const svc::ProcessState new_state = process_info->GetState();
|
||||||
|
|
||||||
|
/* If we're transitioning away from crashed, clear waiting attached. */
|
||||||
|
if (old_state == svc::ProcessState_Crashed && new_state != svc::ProcessState_Crashed) {
|
||||||
|
process_info->ClearExceptionWaitingAttach();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (new_state) {
|
||||||
|
case svc::ProcessState_Created:
|
||||||
|
case svc::ProcessState_CreatedAttached:
|
||||||
|
case svc::ProcessState_Terminating:
|
||||||
|
break;
|
||||||
|
case svc::ProcessState_Running:
|
||||||
|
if (process_info->ShouldSignalOnDebugEvent()) {
|
||||||
|
process_info->ClearSuspended();
|
||||||
|
process_info->SetSuspendedStateChanged();
|
||||||
|
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||||
|
} else if (hos::GetVersion() >= hos::Version_2_0_0 && process_info->ShouldSignalOnStart()) {
|
||||||
|
process_info->SetStartedStateChanged();
|
||||||
|
process_info->ClearSignalOnStart();
|
||||||
|
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||||
|
}
|
||||||
|
process_info->ClearUnhandledException();
|
||||||
|
break;
|
||||||
|
case svc::ProcessState_Crashed:
|
||||||
|
if (!process_info->HasUnhandledException()) {
|
||||||
|
process_info->SetExceptionOccurred();
|
||||||
|
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||||
|
}
|
||||||
|
process_info->SetExceptionWaitingAttach();
|
||||||
|
break;
|
||||||
|
case svc::ProcessState_RunningAttached:
|
||||||
|
if (process_info->ShouldSignalOnDebugEvent()) {
|
||||||
|
process_info->ClearSuspended();
|
||||||
|
process_info->SetSuspendedStateChanged();
|
||||||
|
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||||
|
}
|
||||||
|
process_info->ClearUnhandledException();
|
||||||
|
break;
|
||||||
|
case svc::ProcessState_Terminated:
|
||||||
|
/* Unlink from multi wait. */
|
||||||
|
os::UnlinkMultiWaitHolder(process_info->GetMultiWaitHolder());
|
||||||
|
|
||||||
|
/* Free process resources. */
|
||||||
|
process_info->Cleanup();
|
||||||
|
|
||||||
|
if (hos::GetVersion() < hos::Version_5_0_0 && process_info->ShouldSignalOnExit()) {
|
||||||
|
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||||
|
} else {
|
||||||
|
/* Handle the case where we need to keep the process alive some time longer. */
|
||||||
|
if (hos::GetVersion() >= hos::Version_5_0_0 && process_info->ShouldSignalOnExit()) {
|
||||||
|
/* Remove from the living list. */
|
||||||
|
list->Remove(process_info);
|
||||||
|
|
||||||
|
/* Add the process to the list of dead processes. */
|
||||||
|
{
|
||||||
|
GetExitList()->push_back(*process_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Signal. */
|
||||||
|
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||||
|
} else {
|
||||||
|
/* Actually delete process. */
|
||||||
|
CleanupProcessInfo(list, process_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case svc::ProcessState_DebugBreak:
|
||||||
|
if (process_info->ShouldSignalOnDebugEvent()) {
|
||||||
|
process_info->SetSuspended();
|
||||||
|
process_info->SetSuspendedStateChanged();
|
||||||
|
os::SignalSystemEvent(std::addressof(g_process_event));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateProcessEvent() {
|
||||||
|
/* Create process event. */
|
||||||
|
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_process_event), os::EventClearMode_AutoClear, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetProcessEventHandle(os::NativeHandle *out) {
|
||||||
|
*out = os::GetReadableHandleOfSystemEvent(std::addressof(g_process_event));
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
57
stratosphere/pm/source/impl/pm_process_tracker.hpp
Normal file
57
stratosphere/pm/source/impl/pm_process_tracker.hpp
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "pm_process_info.hpp"
|
||||||
|
|
||||||
|
namespace ams::pm::impl {
|
||||||
|
|
||||||
|
class ProcessTracker {
|
||||||
|
NON_COPYABLE(ProcessTracker);
|
||||||
|
NON_MOVEABLE(ProcessTracker);
|
||||||
|
private:
|
||||||
|
os::ThreadType m_thread;
|
||||||
|
os::EventType m_request_event;
|
||||||
|
os::EventType m_reply_event;
|
||||||
|
os::SdkMutex m_mutex;
|
||||||
|
ProcessInfo *m_queued_process_info;
|
||||||
|
util::Atomic<u32> m_process_count;
|
||||||
|
public:
|
||||||
|
constexpr ProcessTracker() : m_thread(), m_request_event(), m_reply_event(), m_mutex(), m_queued_process_info(nullptr), m_process_count(0) {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize(void *stack, size_t stack_size);
|
||||||
|
void StartThread();
|
||||||
|
|
||||||
|
void QueueEntry(ProcessInfo *process_info);
|
||||||
|
|
||||||
|
u32 GetProcessCount() const {
|
||||||
|
return m_process_count;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void OnProcessSignaled(ProcessInfo *process_info);
|
||||||
|
private:
|
||||||
|
static void ThreadFunction(void *_this) {
|
||||||
|
static_cast<ProcessTracker *>(_this)->ThreadBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadBody();
|
||||||
|
};
|
||||||
|
|
||||||
|
void CreateProcessEvent();
|
||||||
|
|
||||||
|
}
|
|
@ -14,9 +14,9 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "pm_resource_manager.hpp"
|
#include "pm_spec.hpp"
|
||||||
|
|
||||||
namespace ams::pm::resource {
|
namespace ams::pm::impl {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -209,8 +209,8 @@ namespace ams::pm::resource {
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<auto GetResourceLimitValueImpl>
|
template<auto ImplFunction>
|
||||||
ALWAYS_INLINE Result GetResourceLimitValuesImpl(ResourceLimitGroup group, pm::ResourceLimitValue *out) {
|
ALWAYS_INLINE Result GetResourceLimitValueImpl(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
|
||||||
/* Sanity check group. */
|
/* Sanity check group. */
|
||||||
AMS_ABORT_UNLESS(group < ResourceLimitGroup_Count);
|
AMS_ABORT_UNLESS(group < ResourceLimitGroup_Count);
|
||||||
|
|
||||||
|
@ -219,11 +219,11 @@ namespace ams::pm::resource {
|
||||||
|
|
||||||
/* Get values. */
|
/* Get values. */
|
||||||
int64_t values[svc::LimitableResource_Count];
|
int64_t values[svc::LimitableResource_Count];
|
||||||
R_ABORT_UNLESS(GetResourceLimitValueImpl(std::addressof(values[svc::LimitableResource_PhysicalMemoryMax]), handle, svc::LimitableResource_PhysicalMemoryMax));
|
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_PhysicalMemoryMax]), handle, svc::LimitableResource_PhysicalMemoryMax));
|
||||||
R_ABORT_UNLESS(GetResourceLimitValueImpl(std::addressof(values[svc::LimitableResource_ThreadCountMax]), handle, svc::LimitableResource_ThreadCountMax));
|
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_ThreadCountMax]), handle, svc::LimitableResource_ThreadCountMax));
|
||||||
R_ABORT_UNLESS(GetResourceLimitValueImpl(std::addressof(values[svc::LimitableResource_EventCountMax]), handle, svc::LimitableResource_EventCountMax));
|
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_EventCountMax]), handle, svc::LimitableResource_EventCountMax));
|
||||||
R_ABORT_UNLESS(GetResourceLimitValueImpl(std::addressof(values[svc::LimitableResource_TransferMemoryCountMax]), handle, svc::LimitableResource_TransferMemoryCountMax));
|
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_TransferMemoryCountMax]), handle, svc::LimitableResource_TransferMemoryCountMax));
|
||||||
R_ABORT_UNLESS(GetResourceLimitValueImpl(std::addressof(values[svc::LimitableResource_SessionCountMax]), handle, svc::LimitableResource_SessionCountMax));
|
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_SessionCountMax]), handle, svc::LimitableResource_SessionCountMax));
|
||||||
|
|
||||||
/* Set to output. */
|
/* Set to output. */
|
||||||
out->physical_memory = values[svc::LimitableResource_PhysicalMemoryMax];
|
out->physical_memory = values[svc::LimitableResource_PhysicalMemoryMax];
|
||||||
|
@ -278,8 +278,7 @@ namespace ams::pm::resource {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resource API. */
|
Result InitializeSpec() {
|
||||||
Result InitializeResourceManager() {
|
|
||||||
/* Create resource limit handles. */
|
/* Create resource limit handles. */
|
||||||
for (size_t i = 0; i < ResourceLimitGroup_Count; i++) {
|
for (size_t i = 0; i < ResourceLimitGroup_Count; i++) {
|
||||||
if (i == ResourceLimitGroup_System) {
|
if (i == ResourceLimitGroup_System) {
|
||||||
|
@ -459,16 +458,16 @@ namespace ams::pm::resource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetResourceLimitCurrentValue(ResourceLimitGroup group, pm::ResourceLimitValue *out) {
|
Result GetResourceLimitCurrentValue(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
|
||||||
R_RETURN(GetResourceLimitValuesImpl<::ams::svc::GetResourceLimitCurrentValue>(group, out));
|
R_RETURN(GetResourceLimitValueImpl<::ams::svc::GetResourceLimitCurrentValue>(out, group));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetResourceLimitPeakValue(ResourceLimitGroup group, pm::ResourceLimitValue *out) {
|
Result GetResourceLimitPeakValue(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
|
||||||
R_RETURN(GetResourceLimitValuesImpl<::ams::svc::GetResourceLimitPeakValue>(group, out));
|
R_RETURN(GetResourceLimitValueImpl<::ams::svc::GetResourceLimitPeakValue>(out, group));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetResourceLimitLimitValue(ResourceLimitGroup group, pm::ResourceLimitValue *out) {
|
Result GetResourceLimitLimitValue(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
|
||||||
R_RETURN(GetResourceLimitValuesImpl<::ams::svc::GetResourceLimitLimitValue>(group, out));
|
R_RETURN(GetResourceLimitValueImpl<::ams::svc::GetResourceLimitLimitValue>(out, group));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetResourceLimitValues(s64 *out_cur, s64 *out_lim, ResourceLimitGroup group, svc::LimitableResource resource) {
|
Result GetResourceLimitValues(s64 *out_cur, s64 *out_lim, ResourceLimitGroup group, svc::LimitableResource resource) {
|
|
@ -16,10 +16,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
namespace ams::pm::resource {
|
namespace ams::pm::impl {
|
||||||
|
|
||||||
|
Result InitializeSpec();
|
||||||
|
|
||||||
/* Resource API. */
|
|
||||||
Result InitializeResourceManager();
|
|
||||||
Result BoostSystemMemoryResourceLimit(u64 boost_size);
|
Result BoostSystemMemoryResourceLimit(u64 boost_size);
|
||||||
Result BoostApplicationThreadResourceLimit();
|
Result BoostApplicationThreadResourceLimit();
|
||||||
Result BoostSystemThreadResourceLimit();
|
Result BoostSystemThreadResourceLimit();
|
||||||
|
@ -31,9 +31,9 @@ namespace ams::pm::resource {
|
||||||
|
|
||||||
void WaitResourceAvailable(const ldr::ProgramInfo *info);
|
void WaitResourceAvailable(const ldr::ProgramInfo *info);
|
||||||
|
|
||||||
Result GetResourceLimitCurrentValue(ResourceLimitGroup group, pm::ResourceLimitValue *out);
|
Result GetResourceLimitCurrentValue(pm::ResourceLimitValue *out, ResourceLimitGroup group);
|
||||||
Result GetResourceLimitPeakValue(ResourceLimitGroup group, pm::ResourceLimitValue *out);
|
Result GetResourceLimitPeakValue(pm::ResourceLimitValue *outm, ResourceLimitGroup group);
|
||||||
Result GetResourceLimitLimitValue(ResourceLimitGroup group, pm::ResourceLimitValue *out);
|
Result GetResourceLimitLimitValue(pm::ResourceLimitValue *out, ResourceLimitGroup group);
|
||||||
|
|
||||||
Result GetResourceLimitValues(s64 *out_cur, s64 *out_lim, ResourceLimitGroup group, svc::LimitableResource resource);
|
Result GetResourceLimitValues(s64 *out_cur, s64 *out_lim, ResourceLimitGroup group, svc::LimitableResource resource);
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace ams::pm {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/* Global bootmode. */
|
/* Global bootmode. */
|
||||||
BootMode g_boot_mode = BootMode::Normal;
|
constinit BootMode g_boot_mode = BootMode::Normal;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "pm_debug_monitor_service.hpp"
|
#include "pm_debug_monitor_service.hpp"
|
||||||
#include "impl/pm_process_manager.hpp"
|
#include "impl/pm_process_manager.hpp"
|
||||||
|
#include "impl/pm_spec.hpp"
|
||||||
|
|
||||||
namespace ams::pm {
|
namespace ams::pm {
|
||||||
|
|
||||||
|
@ -76,7 +77,7 @@ namespace ams::pm {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result DebugMonitorService::AtmosphereGetCurrentLimitInfo(sf::Out<s64> out_cur_val, sf::Out<s64> out_lim_val, u32 group, u32 resource) {
|
Result DebugMonitorService::AtmosphereGetCurrentLimitInfo(sf::Out<s64> out_cur_val, sf::Out<s64> out_lim_val, u32 group, u32 resource) {
|
||||||
R_RETURN(impl::AtmosphereGetCurrentLimitInfo(out_cur_val.GetPointer(), out_lim_val.GetPointer(), group, resource));
|
R_RETURN(impl::GetResourceLimitValues(out_cur_val.GetPointer(), out_lim_val.GetPointer(), static_cast<ResourceLimitGroup>(group), static_cast<svc::LimitableResource>(resource)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "pm_info_service.hpp"
|
#include "pm_info_service.hpp"
|
||||||
#include "impl/pm_process_manager.hpp"
|
#include "impl/pm_process_manager.hpp"
|
||||||
|
#include "impl/pm_spec.hpp"
|
||||||
|
|
||||||
namespace ams::pm {
|
namespace ams::pm {
|
||||||
|
|
||||||
|
@ -34,11 +35,11 @@ namespace ams::pm {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result InformationService::GetAppletResourceLimitCurrentValue(sf::Out<pm::ResourceLimitValue> out) {
|
Result InformationService::GetAppletResourceLimitCurrentValue(sf::Out<pm::ResourceLimitValue> out) {
|
||||||
R_RETURN(impl::GetAppletResourceLimitCurrentValue(out.GetPointer()));
|
R_RETURN(impl::GetResourceLimitCurrentValue(out.GetPointer(), ResourceLimitGroup_Applet));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result InformationService::GetAppletResourceLimitPeakValue(sf::Out<pm::ResourceLimitValue> out) {
|
Result InformationService::GetAppletResourceLimitPeakValue(sf::Out<pm::ResourceLimitValue> out) {
|
||||||
R_RETURN(impl::GetAppletResourceLimitPeakValue(out.GetPointer()));
|
R_RETURN(impl::GetResourceLimitPeakValue(out.GetPointer(), ResourceLimitGroup_Applet));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Atmosphere extension commands. */
|
/* Atmosphere extension commands. */
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "pm_shell_service.hpp"
|
#include "pm_shell_service.hpp"
|
||||||
#include "impl/pm_process_manager.hpp"
|
#include "impl/pm_process_manager.hpp"
|
||||||
|
#include "impl/pm_spec.hpp"
|
||||||
|
|
||||||
namespace ams::pm {
|
namespace ams::pm {
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue