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

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

View File

@@ -1,35 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::pm::impl {
struct ProcessAttributes {
u8 unknown[3];
ldr::ProgramAttributes program_attrs;
};
static_assert(sizeof(ProcessAttributes) == 5);
constexpr inline ProcessAttributes ProcessAttributes_Nx = {
.unknown = {},
.program_attrs = {
.platform = ncm::ContentMetaPlatform::Nx,
.content_attributes = fs::ContentAttributes_None,
},
};
}

View File

@@ -1,121 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pm_process_info.hpp"
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, const ProcessAttributes &attrs) : 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), m_attrs(attrs) {
os::InitializeMultiWaitHolder(std::addressof(m_multi_wait_holder), m_handle);
os::SetMultiWaitHolderUserData(std::addressof(m_multi_wait_holder), reinterpret_cast<uintptr_t>(this));
}
ProcessInfo::~ProcessInfo() {
this->Cleanup();
}
void ProcessInfo::Cleanup() {
if (m_handle != os::InvalidNativeHandle) {
/* Unregister the process. */
fsprUnregisterProgram(m_process_id.value);
sm::manager::UnregisterProcess(m_process_id);
ldr::pm::UnpinProgram(m_pin_id);
/* Close the process's handle. */
os::CloseNativeHandle(m_handle);
m_handle = os::InvalidNativeHandle;
}
}
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, const ProcessAttributes &attrs) {
return g_process_info_allocator.AllocateProcessInfo(process_handle, process_id, pin_id, location, override_status, attrs);
}
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);
}
}

View File

@@ -1,247 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "pm_process_manager.hpp"
#include "pm_process_attributes.hpp"
namespace ams::pm::impl {
class ProcessList;
class ProcessInfo {
friend class ProcessList;
NON_COPYABLE(ProcessInfo);
NON_MOVEABLE(ProcessInfo);
private:
enum Flag : u32 {
Flag_SignalOnExit = (1 << 0),
Flag_ExceptionOccurred = (1 << 1),
Flag_ExceptionWaitingAttach = (1 << 2),
Flag_SignalOnDebugEvent = (1 << 3),
Flag_SuspendedStateChanged = (1 << 4),
Flag_Suspended = (1 << 5),
Flag_Application = (1 << 6),
Flag_SignalOnStart = (1 << 7),
Flag_StartedStateChanged = (1 << 8),
Flag_UnhandledException = (1 << 9),
};
private:
util::IntrusiveListNode m_list_node;
const os::ProcessId m_process_id;
const ldr::PinId m_pin_id;
const ncm::ProgramLocation m_loc;
const cfg::OverrideStatus m_status;
os::NativeHandle m_handle;
svc::ProcessState m_state;
u32 m_flags;
ProcessAttributes m_attrs;
os::MultiWaitHolderType m_multi_wait_holder;
private:
void SetFlag(Flag flag) {
m_flags |= flag;
}
void ClearFlag(Flag flag) {
m_flags &= ~flag;
}
bool HasFlag(Flag flag) const {
return (m_flags & flag);
}
public:
ProcessInfo(os::NativeHandle h, os::ProcessId pid, ldr::PinId pin, const ncm::ProgramLocation &l, const cfg::OverrideStatus &s, const ProcessAttributes &attrs);
~ProcessInfo();
void Cleanup();
os::MultiWaitHolderType *GetMultiWaitHolder() {
return std::addressof(m_multi_wait_holder);
}
os::NativeHandle GetHandle() const {
return m_handle;
}
os::ProcessId GetProcessId() const {
return m_process_id;
}
ldr::PinId GetPinId() const {
return m_pin_id;
}
const ncm::ProgramLocation &GetProgramLocation() const {
return m_loc;
}
const cfg::OverrideStatus &GetOverrideStatus() const {
return m_status;
}
const ProcessAttributes &GetProcessAttributes() const {
return m_attrs;
}
svc::ProcessState GetState() const {
return m_state;
}
void SetState(svc::ProcessState state) {
m_state = state;
}
bool HasStarted() const {
return m_state != svc::ProcessState_Created && m_state != svc::ProcessState_CreatedAttached;
}
bool HasTerminated() const {
return m_state == svc::ProcessState_Terminated;
}
#define DEFINE_FLAG_SET(flag) \
void Set##flag() { \
this->SetFlag(Flag_##flag); \
}
#define DEFINE_FLAG_GET(get, flag) \
bool get##flag() const { \
return this->HasFlag(Flag_##flag); \
}
#define DEFINE_FLAG_CLEAR(flag) \
void Clear##flag() { \
this->ClearFlag(Flag_##flag); \
}
DEFINE_FLAG_SET(SignalOnExit)
DEFINE_FLAG_GET(Should, SignalOnExit)
/* This needs a manual setter, because it sets two flags. */
void SetExceptionOccurred() {
this->SetFlag(Flag_ExceptionOccurred);
this->SetFlag(Flag_UnhandledException);
}
DEFINE_FLAG_GET(Has, ExceptionOccurred)
DEFINE_FLAG_GET(Has, ExceptionWaitingAttach)
DEFINE_FLAG_GET(Has, UnhandledException)
DEFINE_FLAG_SET(ExceptionWaitingAttach)
DEFINE_FLAG_CLEAR(ExceptionOccurred)
DEFINE_FLAG_CLEAR(ExceptionWaitingAttach)
DEFINE_FLAG_CLEAR(UnhandledException)
DEFINE_FLAG_SET(SignalOnDebugEvent)
DEFINE_FLAG_GET(Should, SignalOnDebugEvent)
DEFINE_FLAG_SET(SuspendedStateChanged)
DEFINE_FLAG_GET(Has, SuspendedStateChanged)
DEFINE_FLAG_CLEAR(SuspendedStateChanged)
DEFINE_FLAG_SET(Suspended)
DEFINE_FLAG_GET(Is, Suspended)
DEFINE_FLAG_CLEAR(Suspended)
DEFINE_FLAG_SET(Application)
DEFINE_FLAG_GET(Is, Application)
DEFINE_FLAG_SET(SignalOnStart)
DEFINE_FLAG_GET(Should, SignalOnStart)
DEFINE_FLAG_CLEAR(SignalOnStart)
DEFINE_FLAG_SET(StartedStateChanged)
DEFINE_FLAG_GET(Has, StartedStateChanged)
DEFINE_FLAG_CLEAR(StartedStateChanged)
#undef DEFINE_FLAG_SET
#undef DEFINE_FLAG_GET
#undef DEFINE_FLAG_CLEAR
};
class ProcessList final : public util::IntrusiveListMemberTraits<&ProcessInfo::m_list_node>::ListType {
private:
os::SdkMutex m_lock;
public:
constexpr ProcessList() : m_lock() { /* ... */ }
void Lock() {
m_lock.Lock();
}
void Unlock() {
m_lock.Unlock();
}
void Remove(ProcessInfo *process_info) {
this->erase(this->iterator_to(*process_info));
}
ProcessInfo *Find(os::ProcessId process_id) {
for (auto &info : *this) {
if (info.GetProcessId() == process_id) {
return std::addressof(info);
}
}
return nullptr;
}
ProcessInfo *Find(ncm::ProgramId program_id) {
for (auto &info : *this) {
if (info.GetProgramLocation().program_id == program_id) {
return std::addressof(info);
}
}
return nullptr;
}
};
class ProcessListAccessor final {
private:
ProcessList &m_list;
public:
explicit ProcessListAccessor(ProcessList &l) : m_list(l) {
m_list.Lock();
}
~ProcessListAccessor() {
m_list.Unlock();
}
ProcessList *operator->() {
return std::addressof(m_list);
}
const ProcessList *operator->() const {
return std::addressof(m_list);
}
ProcessList &operator*() {
return m_list;
}
const ProcessList &operator*() const {
return m_list;
}
};
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, const ProcessAttributes &attrs);
void CleanupProcessInfo(ProcessListAccessor &list, ProcessInfo *process_info);
}

View File

@@ -1,510 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pm_process_manager.hpp"
#include "pm_process_tracker.hpp"
#include "pm_process_info.hpp"
#include "pm_spec.hpp"
namespace ams::pm::impl {
namespace {
/* Types. */
enum HookType {
HookType_ProgramId = (1 << 0),
HookType_Application = (1 << 1),
};
#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) {
const auto hos_version = hos::GetVersion();
return launch_flags & GET_FLAG_MASK(SignalOnExit);
}
inline bool ShouldSignalOnStart(u32 launch_flags) {
const auto hos_version = hos::GetVersion();
if (hos_version < hos::Version_2_0_0) {
return false;
}
return launch_flags & GET_FLAG_MASK(SignalOnStart);
}
inline bool ShouldSignalOnException(u32 launch_flags) {
const auto hos_version = hos::GetVersion();
return launch_flags & GET_FLAG_MASK(SignalOnException);
}
inline bool ShouldSignalOnDebugEvent(u32 launch_flags) {
const auto hos_version = hos::GetVersion();
return launch_flags & GET_FLAG_MASK(SignalOnDebugEvent);
}
inline bool ShouldStartSuspended(u32 launch_flags) {
const auto hos_version = hos::GetVersion();
return launch_flags & GET_FLAG_MASK(StartSuspended);
}
inline bool ShouldDisableAslr(u32 launch_flags) {
const auto hos_version = hos::GetVersion();
return launch_flags & GET_FLAG_MASK(DisableAslr);
}
#undef GET_FLAG_MASK
/* Process Tracking globals. */
constinit ProcessTracker g_process_tracker;
alignas(os::ThreadStackAlignment) constinit u8 g_process_track_thread_stack[8_KB];
/* Global events. */
constinit os::SystemEventType g_hook_to_create_process_event;
constinit os::SystemEventType g_hook_to_create_application_process_event;
constinit os::SystemEventType g_boot_finished_event;
/* Hook globals. */
constinit std::atomic<ncm::ProgramId> g_program_id_hook;
constinit std::atomic<bool> g_application_hook;
/* Helpers. */
void CreateDebuggerEvent() {
/* Create debugger hook events. */
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_process_event), os::EventClearMode_AutoClear, true));
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_application_process_event), os::EventClearMode_AutoClear, true));
}
inline u32 GetLoaderCreateProcessFlags(u32 launch_flags) {
u32 ldr_flags = 0;
if (ShouldSignalOnException(launch_flags) || (hos::GetVersion() >= hos::Version_2_0_0 && !ShouldStartSuspended(launch_flags))) {
ldr_flags |= ldr::CreateProcessFlag_EnableDebug;
}
if (ShouldDisableAslr(launch_flags)) {
ldr_flags |= ldr::CreateProcessFlag_DisableAslr;
}
return ldr_flags;
}
bool HasApplicationProcess() {
auto list = GetProcessList();
for (auto &process : *list) {
if (process.IsApplication()) {
return true;
}
}
return false;
}
Result StartProcess(ProcessInfo *process_info, const ldr::ProgramInfo *program_info) {
R_TRY(svc::StartProcess(process_info->GetHandle(), program_info->main_thread_priority, program_info->default_cpu_id, program_info->main_thread_stack_size));
process_info->SetState(svc::ProcessState_Running);
R_SUCCEED();
}
Result LaunchProgramImpl(ProcessInfo **out_process_info, os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 flags, const ProcessAttributes &attrs) {
/* Set the output to nullptr, if we fail. */
*out_process_info = nullptr;
/* Get Program Info. */
ldr::ProgramInfo program_info;
cfg::OverrideStatus override_status;
R_TRY(ldr::pm::AtmosphereGetProgramInfo(std::addressof(program_info), std::addressof(override_status), loc, attrs.program_attrs));
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;
/* Ensure we only try to run one application. */
R_UNLESS(!is_application || !HasApplicationProcess(), pm::ResultApplicationRunning());
/* Fix the program location to use the right program 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. */
os::NativeHandle process_handle;
ldr::PinId pin_id;
{
/* Pin the program with loader. */
R_TRY(ldr::pm::AtmospherePinProgram(std::addressof(pin_id), fixed_location, override_status));
/* If we fail after now, unpin. */
ON_RESULT_FAILURE { ldr::pm::UnpinProgram(pin_id); };
/* Ensure we can talk to mitm services. */
{
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_initialized_mitm, false);
if (!s_initialized_mitm) {
mitm::pm::Initialize();
s_initialized_mitm = true;
}
}
/* Determine boost size for mitm. */
u64 mitm_boost_size = 0;
R_TRY(mitm::pm::PrepareLaunchProgram(std::addressof(mitm_boost_size), program_info.program_id, override_status, is_application));
if (mitm_boost_size > 0 || is_application) {
R_ABORT_UNLESS(BoostSystemMemoryResourceLimitForMitm(mitm_boost_size));
}
ON_RESULT_FAILURE_2 { if (mitm_boost_size > 0 || is_application) { R_ABORT_UNLESS(BoostSystemMemoryResourceLimitForMitm(0)); } };
/* Ensure resources are available. */
WaitResourceAvailable(std::addressof(program_info));
/* Actually create the process. */
R_TRY(ldr::pm::CreateProcess(std::addressof(process_handle), pin_id, GetLoaderCreateProcessFlags(flags), GetResourceLimitHandle(std::addressof(program_info)), attrs.program_attrs));
}
/* Get the process id. */
os::ProcessId process_id = os::GetProcessId(process_handle);
/* Make new process info. */
ProcessInfo *process_info = AllocateProcessInfo(process_handle, process_id, pin_id, fixed_location, override_status, attrs);
AMS_ABORT_UNLESS(process_info != nullptr);
/* Add the new process info to the process list. */
{
auto list = GetProcessList();
list->push_back(*process_info);
}
/* Prevent resource leakage if register fails. */
ON_RESULT_FAILURE {
auto list = GetProcessList();
process_info->Cleanup();
CleanupProcessInfo(list, process_info);
};
const u8 *acid_sac = program_info.ac_buffer;
const u8 *aci_sac = acid_sac + program_info.acid_sac_size;
const u8 *acid_fac = aci_sac + program_info.aci_sac_size;
const u8 *aci_fah = acid_fac + program_info.acid_fac_size;
/* Register with FS and SM. */
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, 0));
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. */
if (is_application) {
process_info->SetApplication();
}
if (ShouldSignalOnStart(flags) && allow_debug) {
process_info->SetSignalOnStart();
}
if (ShouldSignalOnExit(flags)) {
process_info->SetSignalOnExit();
}
if (ShouldSignalOnDebugEvent(flags) && allow_debug) {
process_info->SetSignalOnDebugEvent();
}
/* Process hooks/signaling. */
if (fixed_location.program_id == g_program_id_hook) {
os::SignalSystemEvent(std::addressof(g_hook_to_create_process_event));
g_program_id_hook = ncm::InvalidProgramId;
} else if (is_application && g_application_hook) {
os::SignalSystemEvent(std::addressof(g_hook_to_create_application_process_event));
g_application_hook = false;
} else if (!ShouldStartSuspended(flags)) {
R_TRY(StartProcess(process_info, std::addressof(program_info)));
}
*out_process_id = process_id;
*out_process_info = process_info;
R_SUCCEED();
}
}
/* Initialization. */
Result InitializeProcessManager() {
/* Create events. */
CreateProcessEvent();
CreateDebuggerEvent();
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_boot_finished_event), os::EventClearMode_AutoClear, true));
/* Initialize resource limits. */
R_TRY(InitializeSpec());
/* Initialize the process tracker. */
g_process_tracker.Initialize(g_process_track_thread_stack, sizeof(g_process_track_thread_stack));
/* Start the process tracker thread. */
g_process_tracker.StartThread();
R_SUCCEED();
}
/* Process Management. */
Result LaunchProgram(os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 flags) {
/* Launch the program. */
ProcessInfo *process_info = nullptr;
R_TRY(LaunchProgramImpl(std::addressof(process_info), out_process_id, loc, flags, ProcessAttributes_Nx));
/* Register the process info with the tracker. */
g_process_tracker.QueueEntry(process_info);
R_SUCCEED();
}
Result StartProcess(os::ProcessId process_id) {
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
R_UNLESS(!process_info->HasStarted(), pm::ResultAlreadyStarted());
ldr::ProgramInfo program_info;
R_TRY(ldr::pm::GetProgramInfo(std::addressof(program_info), process_info->GetProgramLocation(), process_info->GetProcessAttributes().program_attrs));
R_RETURN(StartProcess(process_info, std::addressof(program_info)));
}
Result TerminateProcess(os::ProcessId process_id) {
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
R_RETURN(svc::TerminateProcess(process_info->GetHandle()));
}
Result TerminateProgram(ncm::ProgramId program_id) {
auto list = GetProcessList();
auto process_info = list->Find(program_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
R_RETURN(svc::TerminateProcess(process_info->GetHandle()));
}
Result GetProcessEventInfo(ProcessEventInfo *out) {
/* Check for event from current process. */
{
auto list = GetProcessList();
for (auto &process : *list) {
if (process.HasStarted() && process.HasStartedStateChanged()) {
process.ClearStartedStateChanged();
out->event = GetProcessEventValue(ProcessEvent::Started);
out->process_id = process.GetProcessId();
R_SUCCEED();
}
if (process.HasSuspendedStateChanged()) {
process.ClearSuspendedStateChanged();
if (process.IsSuspended()) {
out->event = GetProcessEventValue(ProcessEvent::DebugBreak);
} else {
out->event = GetProcessEventValue(ProcessEvent::DebugRunning);
}
out->process_id = process.GetProcessId();
R_SUCCEED();
}
if (process.HasExceptionOccurred()) {
process.ClearExceptionOccurred();
out->event = GetProcessEventValue(ProcessEvent::Exception);
out->process_id = process.GetProcessId();
R_SUCCEED();
}
if (hos::GetVersion() < hos::Version_5_0_0 && process.ShouldSignalOnExit() && process.HasTerminated()) {
out->event = GetProcessEventValue(ProcessEvent::Exited);
out->process_id = process.GetProcessId();
R_SUCCEED();
}
}
}
/* Check for event from exited process. */
if (hos::GetVersion() >= hos::Version_5_0_0) {
auto exit_list = GetExitList();
if (!exit_list->empty()) {
auto &process_info = exit_list->front();
out->event = GetProcessEventValue(ProcessEvent::Exited);
out->process_id = process_info.GetProcessId();
CleanupProcessInfo(exit_list, std::addressof(process_info));
R_SUCCEED();
}
}
out->process_id = os::ProcessId{};
out->event = GetProcessEventValue(ProcessEvent::None);
R_SUCCEED();
}
Result CleanupProcess(os::ProcessId process_id) {
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
R_UNLESS(process_info->HasTerminated(), pm::ResultNotTerminated());
CleanupProcessInfo(list, process_info);
R_SUCCEED();
}
Result ClearExceptionOccurred(os::ProcessId process_id) {
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
process_info->ClearExceptionOccurred();
R_SUCCEED();
}
/* Information Getters. */
Result GetModuleIdList(u32 *out_count, u8 *out_buf, size_t max_out_count, u64 unused) {
/* This function was always stubbed... */
AMS_UNUSED(out_buf, max_out_count, unused);
*out_count = 0;
R_SUCCEED();
}
Result GetExceptionProcessIdList(u32 *out_count, os::ProcessId *out_process_ids, size_t max_out_count) {
auto list = GetProcessList();
size_t count = 0;
if (max_out_count > 0) {
for (auto &process : *list) {
if (process.HasExceptionWaitingAttach()) {
out_process_ids[count++] = process.GetProcessId();
if (count >= max_out_count) {
break;
}
}
}
}
*out_count = static_cast<u32>(count);
R_SUCCEED();
}
Result GetProcessId(os::ProcessId *out, ncm::ProgramId program_id) {
auto list = GetProcessList();
auto process_info = list->Find(program_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
*out = process_info->GetProcessId();
R_SUCCEED();
}
Result GetProgramId(ncm::ProgramId *out, os::ProcessId process_id) {
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
*out = process_info->GetProgramLocation().program_id;
R_SUCCEED();
}
Result GetApplicationProcessId(os::ProcessId *out_process_id) {
auto list = GetProcessList();
for (auto &process : *list) {
if (process.IsApplication()) {
*out_process_id = process.GetProcessId();
R_SUCCEED();
}
}
R_THROW(pm::ResultProcessNotFound());
}
Result AtmosphereGetProcessInfo(os::NativeHandle *out_process_handle, ncm::ProgramLocation *out_loc, cfg::OverrideStatus *out_status, os::ProcessId process_id) {
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
*out_process_handle = process_info->GetHandle();
*out_loc = process_info->GetProgramLocation();
*out_status = process_info->GetOverrideStatus();
R_SUCCEED();
}
/* Hook API. */
Result HookToCreateProcess(os::NativeHandle *out_hook, ncm::ProgramId program_id) {
*out_hook = os::InvalidNativeHandle;
{
ncm::ProgramId old_value = ncm::InvalidProgramId;
R_UNLESS(g_program_id_hook.compare_exchange_strong(old_value, program_id), pm::ResultDebugHookInUse());
}
*out_hook = os::GetReadableHandleOfSystemEvent(std::addressof(g_hook_to_create_process_event));
R_SUCCEED();
}
Result HookToCreateApplicationProcess(os::NativeHandle *out_hook) {
*out_hook = os::InvalidNativeHandle;
{
bool old_value = false;
R_UNLESS(g_application_hook.compare_exchange_strong(old_value, true), pm::ResultDebugHookInUse());
}
*out_hook = os::GetReadableHandleOfSystemEvent(std::addressof(g_hook_to_create_application_process_event));
R_SUCCEED();
}
Result ClearHook(u32 which) {
if (which & HookType_ProgramId) {
g_program_id_hook = ncm::InvalidProgramId;
}
if (which & HookType_Application) {
g_application_hook = false;
}
R_SUCCEED();
}
/* Boot API. */
Result NotifyBootFinished() {
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_has_boot_finished, false);
if (!s_has_boot_finished) {
/* Set program verification disabled, if we should. */
/* NOTE: Nintendo does not check the result of this. */
if (spl::IsDisabledProgramVerification()) {
if (hos::GetVersion() >= hos::Version_10_0_0) {
ldr::pm::SetEnabledProgramVerification(false);
} else {
fsprSetEnabledProgramVerification(false);
}
}
boot2::LaunchPreSdCardBootProgramsAndBoot2();
s_has_boot_finished = true;
os::SignalSystemEvent(std::addressof(g_boot_finished_event));
}
R_SUCCEED();
}
Result GetBootFinishedEventHandle(os::NativeHandle *out) {
/* In 8.0.0, Nintendo added this command, which signals that the boot sysmodule has finished. */
/* Nintendo only signals it in safe mode FIRM, and this function aborts on normal FIRM. */
/* We will signal it always, but only allow this function to succeed on safe mode. */
AMS_ABORT_UNLESS(spl::IsRecoveryBoot());
*out = os::GetReadableHandleOfSystemEvent(std::addressof(g_boot_finished_event));
R_SUCCEED();
}
}

View File

@@ -1,51 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::pm::impl {
/* Initialization. */
Result InitializeProcessManager();
/* Process Management. */
Result LaunchProgram(os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 flags);
Result StartProcess(os::ProcessId process_id);
Result TerminateProcess(os::ProcessId process_id);
Result TerminateProgram(ncm::ProgramId program_id);
Result GetProcessEventHandle(os::NativeHandle *out);
Result GetProcessEventInfo(ProcessEventInfo *out);
Result CleanupProcess(os::ProcessId process_id);
Result ClearExceptionOccurred(os::ProcessId process_id);
/* Information Getters. */
Result GetModuleIdList(u32 *out_count, u8 *out_buf, size_t max_out_count, u64 unused);
Result GetExceptionProcessIdList(u32 *out_count, os::ProcessId *out_process_ids, size_t max_out_count);
Result GetProcessId(os::ProcessId *out, ncm::ProgramId program_id);
Result GetProgramId(ncm::ProgramId *out, os::ProcessId process_id);
Result GetApplicationProcessId(os::ProcessId *out_process_id);
Result AtmosphereGetProcessInfo(os::NativeHandle *out_process_handle, ncm::ProgramLocation *out_loc, cfg::OverrideStatus *out_status, os::ProcessId process_id);
/* Hook API. */
Result HookToCreateProcess(os::NativeHandle *out_hook, ncm::ProgramId program_id);
Result HookToCreateApplicationProcess(os::NativeHandle *out_hook);
Result ClearHook(u32 which);
/* Boot API. */
Result NotifyBootFinished();
Result GetBootFinishedEventHandle(os::NativeHandle *out);
}

View File

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

View File

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

View File

@@ -1,491 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pm_spec.hpp"
namespace ams::pm::impl {
namespace {
constexpr svc::LimitableResource LimitableResources[] = {
svc::LimitableResource_PhysicalMemoryMax,
svc::LimitableResource_ThreadCountMax,
svc::LimitableResource_EventCountMax,
svc::LimitableResource_TransferMemoryCountMax,
svc::LimitableResource_SessionCountMax,
};
/* Definitions for limit differences over time. */
constexpr size_t ExtraSystemMemorySize400 = 10_MB;
constexpr size_t ReservedMemorySize600 = 5_MB;
/* Atmosphere always allocates extra memory for system usage. */
constexpr size_t ExtraSystemMemorySizeAtmosphere = 32_MB;
/* Desired extra threads. */
constexpr u64 BaseApplicationThreads = 96;
constexpr u64 BaseAppletThreads = 96;
constexpr u64 BaseSystemThreads = 800 - BaseAppletThreads - BaseApplicationThreads;
constexpr s64 ExtraSystemThreads = 1024 - BaseSystemThreads;
constexpr s64 ExtraApplicationThreads = 256 - BaseApplicationThreads;
constexpr s64 ExtraAppletThreads = 256 - BaseAppletThreads;
static_assert(ExtraSystemThreads >= 0);
static_assert(ExtraApplicationThreads >= 0);
static_assert(ExtraAppletThreads >= 0);
/* Globals. */
constinit os::SdkMutex g_resource_limit_lock;
constinit os::NativeHandle g_resource_limit_handles[ResourceLimitGroup_Count];
constinit spl::MemoryArrangement g_memory_arrangement = spl::MemoryArrangement_Standard;
constinit u64 g_extra_threads_available[ResourceLimitGroup_Count];
constinit os::SdkMutex g_system_memory_boost_lock;
constinit u64 g_system_memory_boost_size = 0;
constinit u64 g_system_memory_boost_size_for_mitm = 0;
ALWAYS_INLINE u64 GetCurrentSystemMemoryBoostSize() {
return g_system_memory_boost_size + g_system_memory_boost_size_for_mitm;
}
constinit u64 g_resource_limits[ResourceLimitGroup_Count][svc::LimitableResource_Count] = {
[ResourceLimitGroup_System] = {
[svc::LimitableResource_PhysicalMemoryMax] = 0, /* Initialized dynamically later. */
[svc::LimitableResource_ThreadCountMax] = BaseSystemThreads,
[svc::LimitableResource_EventCountMax] = 0, /* Initialized dynamically later. */
[svc::LimitableResource_TransferMemoryCountMax] = 0, /* Initialized dynamically later. */
[svc::LimitableResource_SessionCountMax] = 0, /* Initialized dynamically later. */
},
[ResourceLimitGroup_Application] = {
[svc::LimitableResource_PhysicalMemoryMax] = 0, /* Initialized dynamically later. */
[svc::LimitableResource_ThreadCountMax] = BaseApplicationThreads,
[svc::LimitableResource_EventCountMax] = 0,
[svc::LimitableResource_TransferMemoryCountMax] = 32,
[svc::LimitableResource_SessionCountMax] = 1,
},
[ResourceLimitGroup_Applet] = {
[svc::LimitableResource_PhysicalMemoryMax] = 0, /* Initialized dynamically later. */
[svc::LimitableResource_ThreadCountMax] = BaseAppletThreads,
[svc::LimitableResource_EventCountMax] = 0,
[svc::LimitableResource_TransferMemoryCountMax] = 32,
[svc::LimitableResource_SessionCountMax] = 5 + 1, /* Add a session for atmosphere's memlet system module. */
},
};
constinit u64 g_memory_resource_limits[spl::MemoryArrangement_Count][ResourceLimitGroup_Count] = {
[spl::MemoryArrangement_Standard] = {
[ResourceLimitGroup_System] = 269_MB,
[ResourceLimitGroup_Application] = 3285_MB,
[ResourceLimitGroup_Applet] = 535_MB,
},
[spl::MemoryArrangement_StandardForAppletDev] = {
[ResourceLimitGroup_System] = 481_MB,
[ResourceLimitGroup_Application] = 2048_MB,
[ResourceLimitGroup_Applet] = 1560_MB,
},
[spl::MemoryArrangement_StandardForSystemDev] = {
[ResourceLimitGroup_System] = 328_MB,
[ResourceLimitGroup_Application] = 3285_MB,
[ResourceLimitGroup_Applet] = 476_MB,
},
[spl::MemoryArrangement_Expanded] = {
[ResourceLimitGroup_System] = 653_MB,
[ResourceLimitGroup_Application] = 4916_MB,
[ResourceLimitGroup_Applet] = 568_MB,
},
[spl::MemoryArrangement_ExpandedForAppletDev] = {
[ResourceLimitGroup_System] = 653_MB,
[ResourceLimitGroup_Application] = 3285_MB,
[ResourceLimitGroup_Applet] = 2199_MB,
},
};
/* Helpers. */
Result SetMemoryResourceLimitLimitValue(ResourceLimitGroup group, u64 new_memory_limit) {
const u64 old_memory_limit = g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax];
g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax] = new_memory_limit;
/* Restore the old memory limit if we fail. */
ON_RESULT_FAILURE { g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax] = old_memory_limit; };
/* Set the resource limit. */
R_RETURN(svc::SetResourceLimitLimitValue(GetResourceLimitHandle(group), svc::LimitableResource_PhysicalMemoryMax, g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax]));
}
Result SetResourceLimitLimitValues(ResourceLimitGroup group, u64 new_memory_limit) {
/* First, set memory limit. */
R_TRY(SetMemoryResourceLimitLimitValue(group, new_memory_limit));
/* Set other limit values. */
for (size_t i = 0; i < svc::LimitableResource_Count; i++) {
const auto resource = LimitableResources[i];
if (resource == svc::LimitableResource_PhysicalMemoryMax) {
continue;
}
R_TRY(svc::SetResourceLimitLimitValue(GetResourceLimitHandle(group), resource, g_resource_limits[group][resource]));
}
R_SUCCEED();
}
inline ResourceLimitGroup GetResourceLimitGroup(const ldr::ProgramInfo *info) {
switch (info->flags & ldr::ProgramInfoFlag_ApplicationTypeMask) {
case ldr::ProgramInfoFlag_Application:
return ResourceLimitGroup_Application;
case ldr::ProgramInfoFlag_Applet:
return ResourceLimitGroup_Applet;
default:
return ResourceLimitGroup_System;
}
}
void WaitResourceAvailable(ResourceLimitGroup group) {
const auto reslimit_hnd = GetResourceLimitHandle(group);
for (size_t i = 0; i < svc::LimitableResource_Count; i++) {
const auto resource = LimitableResources[i];
s64 value = 0;
while (true) {
R_ABORT_UNLESS(svc::GetResourceLimitCurrentValue(&value, reslimit_hnd, resource));
if (value == 0) {
break;
}
os::SleepThread(TimeSpan::FromMilliSeconds(1));
}
}
}
void WaitApplicationMemoryAvailable() {
/* Get firmware version. */
const auto fw_ver = hos::GetVersion();
/* On 15.0.0+, pm considers application memory to be available if there is exactly 96 MB outstanding. */
/* This is probably because this corresponds to the gameplay-recording memory. */
constexpr u64 AllowedUsedApplicationMemory = 96_MB;
/* Wait for memory to be available. */
u64 value = 0;
while (true) {
R_ABORT_UNLESS(svc::GetSystemInfo(&value, svc::SystemInfoType_UsedPhysicalMemorySize, svc::InvalidHandle, svc::PhysicalMemorySystemInfo_Application));
if (value == 0 || (fw_ver >= hos::Version_15_0_0 && value == AllowedUsedApplicationMemory)) {
break;
}
os::SleepThread(TimeSpan::FromMilliSeconds(1));
}
}
bool IsKTraceEnabled() {
u64 value = 0;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), svc::InfoType_MesosphereMeta, svc::InvalidHandle, svc::MesosphereMetaInfo_IsKTraceEnabled));
return value != 0;
}
ALWAYS_INLINE Result BoostThreadResourceLimitLocked(ResourceLimitGroup group) {
AMS_ASSERT(g_resource_limit_lock.IsLockedByCurrentThread());
/* Set new limit. */
const s64 new_thread_count = g_resource_limits[group][svc::LimitableResource_ThreadCountMax] + g_extra_threads_available[group];
R_TRY(svc::SetResourceLimitLimitValue(GetResourceLimitHandle(group), svc::LimitableResource_ThreadCountMax, new_thread_count));
/* Record that we did so. */
g_resource_limits[group][svc::LimitableResource_ThreadCountMax] = new_thread_count;
g_extra_threads_available[group] = 0;
R_SUCCEED();
}
template<auto ImplFunction>
ALWAYS_INLINE Result GetResourceLimitValueImpl(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
/* Sanity check group. */
AMS_ABORT_UNLESS(group < ResourceLimitGroup_Count);
/* Get handle. */
const auto handle = GetResourceLimitHandle(group);
/* Get values. */
int64_t values[svc::LimitableResource_Count];
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_PhysicalMemoryMax]), handle, svc::LimitableResource_PhysicalMemoryMax));
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_ThreadCountMax]), handle, svc::LimitableResource_ThreadCountMax));
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_EventCountMax]), handle, svc::LimitableResource_EventCountMax));
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_TransferMemoryCountMax]), handle, svc::LimitableResource_TransferMemoryCountMax));
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_SessionCountMax]), handle, svc::LimitableResource_SessionCountMax));
/* Set to output. */
out->physical_memory = values[svc::LimitableResource_PhysicalMemoryMax];
out->thread_count = values[svc::LimitableResource_ThreadCountMax];
out->event_count = values[svc::LimitableResource_EventCountMax];
out->transfer_memory_count = values[svc::LimitableResource_TransferMemoryCountMax];
out->session_count = values[svc::LimitableResource_SessionCountMax];
R_SUCCEED();
}
Result BoostSystemMemoryResourceLimitLocked(u64 normal_boost, u64 mitm_boost) {
/* Check pre-conditions. */
AMS_ASSERT(g_system_memory_boost_lock.IsLockedByCurrentThread());
/* Determine total boost. */
const u64 boost_size = normal_boost + mitm_boost;
/* Don't allow all application memory to be taken away. */
R_UNLESS(boost_size < g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application], pm::ResultInvalidSize());
const u64 new_app_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application] - boost_size;
{
std::scoped_lock lk(g_resource_limit_lock);
const auto cur_boost_size = GetCurrentSystemMemoryBoostSize();
if (hos::GetVersion() >= hos::Version_5_0_0) {
/* Starting in 5.0.0, PM does not allow for only one of the sets to fail. */
if (boost_size < cur_boost_size) {
R_TRY(svc::SetUnsafeLimit(boost_size));
R_ABORT_UNLESS(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
} else if (boost_size > cur_boost_size) {
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
R_ABORT_UNLESS(svc::SetUnsafeLimit(boost_size));
} else {
/* If the boost size is equal, there's nothing to do. */
}
} else {
const u64 new_sys_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_System] + boost_size;
if (boost_size < cur_boost_size) {
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System, new_sys_size));
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
} else if (boost_size > cur_boost_size) {
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System, new_sys_size));
} else {
/* If the boost size is equal, there's nothing to do. */
}
}
g_system_memory_boost_size = normal_boost;
g_system_memory_boost_size_for_mitm = mitm_boost;
}
R_SUCCEED();
}
}
Result InitializeSpec() {
/* Create resource limit handles. */
for (size_t i = 0; i < ResourceLimitGroup_Count; i++) {
if (i == ResourceLimitGroup_System) {
u64 value = 0;
R_ABORT_UNLESS(svc::GetInfo(&value, svc::InfoType_ResourceLimit, svc::InvalidHandle, 0));
g_resource_limit_handles[i] = static_cast<svc::Handle>(value);
} else {
R_ABORT_UNLESS(svc::CreateResourceLimit(g_resource_limit_handles + i));
}
}
/* Adjust memory limits based on hos firmware version. */
const auto hos_version = hos::GetVersion();
if (hos_version >= hos::Version_4_0_0) {
/* 4.0.0 took memory away from applet and gave it to system, for the Standard and StandardForSystemDev profiles. */
g_memory_resource_limits[spl::MemoryArrangement_Standard][ResourceLimitGroup_System] += ExtraSystemMemorySize400;
g_memory_resource_limits[spl::MemoryArrangement_Standard][ResourceLimitGroup_Applet] -= ExtraSystemMemorySize400;
g_memory_resource_limits[spl::MemoryArrangement_StandardForSystemDev][ResourceLimitGroup_System] += ExtraSystemMemorySize400;
g_memory_resource_limits[spl::MemoryArrangement_StandardForSystemDev][ResourceLimitGroup_Applet] -= ExtraSystemMemorySize400;
}
/* Determine system resource counts. */
{
/* Get the total resource counts. */
s64 total_events, total_transfer_memories, total_sessions;
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(std::addressof(total_events), GetResourceLimitHandle(ResourceLimitGroup_System), svc::LimitableResource_EventCountMax));
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(std::addressof(total_transfer_memories), GetResourceLimitHandle(ResourceLimitGroup_System), svc::LimitableResource_TransferMemoryCountMax));
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(std::addressof(total_sessions), GetResourceLimitHandle(ResourceLimitGroup_System), svc::LimitableResource_SessionCountMax));
/* Determine system counts. */
const s64 sys_events = total_events - (g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_EventCountMax] + g_resource_limits[ResourceLimitGroup_Applet][svc::LimitableResource_EventCountMax]);
const s64 sys_transfer_memories = total_transfer_memories - (g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_TransferMemoryCountMax] + g_resource_limits[ResourceLimitGroup_Applet][svc::LimitableResource_TransferMemoryCountMax]);
const s64 sys_sessions = total_sessions - (g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_SessionCountMax] + g_resource_limits[ResourceLimitGroup_Applet][svc::LimitableResource_SessionCountMax]);
/* Check system counts. */
AMS_ABORT_UNLESS(sys_events >= 0);
AMS_ABORT_UNLESS(sys_transfer_memories >= 0);
AMS_ABORT_UNLESS(sys_sessions >= 0);
/* Set system counts. */
g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_EventCountMax] = sys_events;
g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_TransferMemoryCountMax] = sys_transfer_memories;
g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_SessionCountMax] = sys_sessions;
}
/* Determine extra application threads. */
{
/* Get total threads available. */
s64 total_threads;
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(std::addressof(total_threads), GetResourceLimitHandle(ResourceLimitGroup_System), svc::LimitableResource_ThreadCountMax));
/* Check that we have enough threads. */
const s64 required_threads = g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_ThreadCountMax] + g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_ThreadCountMax] + g_resource_limits[ResourceLimitGroup_Applet][svc::LimitableResource_ThreadCountMax];
AMS_ABORT_UNLESS(total_threads >= required_threads);
/* Set the number of extra threads. */
const s64 extra_threads = total_threads - required_threads;
if constexpr (true /* TODO: Should we expose the old "all extra threads are application" behavior? Seems pointless. */) {
if (extra_threads > 0) {
/* If we have any extra threads at all, require that we have enough. */
AMS_ABORT_UNLESS(extra_threads >= (ExtraSystemThreads + ExtraApplicationThreads + ExtraAppletThreads));
g_extra_threads_available[ResourceLimitGroup_System] += ExtraSystemThreads;
g_extra_threads_available[ResourceLimitGroup_Application] += ExtraApplicationThreads;
g_extra_threads_available[ResourceLimitGroup_Applet] += ExtraAppletThreads;
}
} else {
g_extra_threads_available[ResourceLimitGroup_Application] = extra_threads;
}
}
/* Choose and initialize memory arrangement. */
const bool use_dynamic_memory_arrangement = (hos_version >= hos::Version_5_0_0);
if (use_dynamic_memory_arrangement) {
/* 6.0.0 retrieves memory limit information from the kernel, rather than using a hardcoded profile. */
g_memory_arrangement = spl::MemoryArrangement_Dynamic;
/* Get total memory available. */
s64 total_memory = 0;
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(&total_memory, GetResourceLimitHandle(ResourceLimitGroup_System), svc::LimitableResource_PhysicalMemoryMax));
/* Get and save application + applet memory. */
R_ABORT_UNLESS(svc::GetSystemInfo(std::addressof(g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Application]), svc::SystemInfoType_TotalPhysicalMemorySize, svc::InvalidHandle, svc::PhysicalMemorySystemInfo_Application));
R_ABORT_UNLESS(svc::GetSystemInfo(std::addressof(g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Applet]), svc::SystemInfoType_TotalPhysicalMemorySize, svc::InvalidHandle, svc::PhysicalMemorySystemInfo_Applet));
const s64 application_size = g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Application];
const s64 applet_size = g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Applet];
const s64 reserved_non_system_size = (application_size + applet_size + ReservedMemorySize600);
/* Ensure there's enough memory for the system region. */
AMS_ABORT_UNLESS(reserved_non_system_size < total_memory);
g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_System] = total_memory - reserved_non_system_size;
} else {
/* Older system versions retrieve memory arrangement from spl, and use hardcoded profiles. */
g_memory_arrangement = spl::GetMemoryArrangement();
/* Adjust memory limits for atmosphere. */
/* We take memory away from applet normally, but away from application on < 3.0.0 to avoid a rare hang on boot. */
const size_t extra_memory_size = ExtraSystemMemorySizeAtmosphere;
const auto src_group = hos_version >= hos::Version_3_0_0 ? ResourceLimitGroup_Applet : ResourceLimitGroup_Application;
for (size_t i = 0; i < spl::MemoryArrangement_Count; i++) {
g_memory_resource_limits[i][ResourceLimitGroup_System] += extra_memory_size;
g_memory_resource_limits[i][src_group] -= extra_memory_size;
}
/* If KTrace is enabled, account for that by subtracting the memory from the applet pool. */
if (IsKTraceEnabled()) {
constexpr size_t KTraceBufferSize = 16_MB;
for (size_t i = 0; i < spl::MemoryArrangement_Count; i++) {
g_memory_resource_limits[i][ResourceLimitGroup_Applet] -= KTraceBufferSize;
}
}
}
/* Actually set resource limits. */
{
std::scoped_lock lk(g_resource_limit_lock);
for (size_t group = 0; group < ResourceLimitGroup_Count; group++) {
R_ABORT_UNLESS(SetResourceLimitLimitValues(static_cast<ResourceLimitGroup>(group), g_memory_resource_limits[g_memory_arrangement][group]));
}
}
R_SUCCEED();
}
Result BoostSystemMemoryResourceLimit(u64 boost_size) {
/* Ensure only one boost change happens at a time. */
std::scoped_lock lk(g_system_memory_boost_lock);
/* Boost to the appropriate total amount. */
R_RETURN(BoostSystemMemoryResourceLimitLocked(boost_size, g_system_memory_boost_size_for_mitm));
}
Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size) {
/* Ensure only one boost change happens at a time. */
std::scoped_lock lk(g_system_memory_boost_lock);
/* Boost to the appropriate total amount. */
R_RETURN(BoostSystemMemoryResourceLimitLocked(g_system_memory_boost_size, boost_size));
}
Result BoostApplicationThreadResourceLimit() {
std::scoped_lock lk(g_resource_limit_lock);
/* Boost the limit. */
R_TRY(BoostThreadResourceLimitLocked(ResourceLimitGroup_Application));
R_SUCCEED();
}
Result BoostSystemThreadResourceLimit() {
std::scoped_lock lk(g_resource_limit_lock);
/* Boost the limits. */
R_TRY(BoostThreadResourceLimitLocked(ResourceLimitGroup_Applet));
R_TRY(BoostThreadResourceLimitLocked(ResourceLimitGroup_System));
R_SUCCEED();
}
os::NativeHandle GetResourceLimitHandle(ResourceLimitGroup group) {
return g_resource_limit_handles[group];
}
os::NativeHandle GetResourceLimitHandle(const ldr::ProgramInfo *info) {
return GetResourceLimitHandle(GetResourceLimitGroup(info));
}
void WaitResourceAvailable(const ldr::ProgramInfo *info) {
if (GetResourceLimitGroup(info) == ResourceLimitGroup_Application) {
WaitResourceAvailable(ResourceLimitGroup_Application);
if (hos::GetVersion() >= hos::Version_5_0_0) {
WaitApplicationMemoryAvailable();
}
}
}
Result GetResourceLimitCurrentValue(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
R_RETURN(GetResourceLimitValueImpl<::ams::svc::GetResourceLimitCurrentValue>(out, group));
}
Result GetResourceLimitPeakValue(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
R_RETURN(GetResourceLimitValueImpl<::ams::svc::GetResourceLimitPeakValue>(out, group));
}
Result GetResourceLimitLimitValue(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
R_RETURN(GetResourceLimitValueImpl<::ams::svc::GetResourceLimitLimitValue>(out, group));
}
Result GetResourceLimitValues(s64 *out_cur, s64 *out_lim, ResourceLimitGroup group, svc::LimitableResource resource) {
/* Do not allow out of bounds access. */
AMS_ABORT_UNLESS(group < ResourceLimitGroup_Count);
AMS_ABORT_UNLESS(resource < svc::LimitableResource_Count);
const auto reslimit_hnd = GetResourceLimitHandle(group);
R_TRY(svc::GetResourceLimitCurrentValue(out_cur, reslimit_hnd, resource));
R_TRY(svc::GetResourceLimitLimitValue(out_lim, reslimit_hnd, resource));
R_SUCCEED();
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::pm::impl {
Result InitializeSpec();
Result BoostSystemMemoryResourceLimit(u64 boost_size);
Result BoostApplicationThreadResourceLimit();
Result BoostSystemThreadResourceLimit();
Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size);
os::NativeHandle GetResourceLimitHandle(ResourceLimitGroup group);
os::NativeHandle GetResourceLimitHandle(const ldr::ProgramInfo *info);
void WaitResourceAvailable(const ldr::ProgramInfo *info);
Result GetResourceLimitCurrentValue(pm::ResourceLimitValue *out, ResourceLimitGroup group);
Result GetResourceLimitPeakValue(pm::ResourceLimitValue *outm, ResourceLimitGroup group);
Result GetResourceLimitLimitValue(pm::ResourceLimitValue *out, ResourceLimitGroup group);
Result GetResourceLimitValues(s64 *out_cur, s64 *out_lim, ResourceLimitGroup group, svc::LimitableResource resource);
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pm_boot_mode_service.hpp"
namespace ams::pm {
namespace {
/* Global bootmode. */
constinit BootMode g_boot_mode = BootMode::Normal;
constinit u32 g_unknown = 0;
}
/* Override of weakly linked boot_mode_api functions. */
namespace bm {
BootMode GetBootMode() {
return g_boot_mode;
}
void SetMaintenanceBoot() {
g_boot_mode = BootMode::Maintenance;
}
}
/* Service command implementations. */
void BootModeService::GetBootMode(sf::Out<u32> out) {
out.SetValue(static_cast<u32>(pm::bm::GetBootMode()));
}
void BootModeService::SetMaintenanceBoot() {
pm::bm::SetMaintenanceBoot();
}
void BootModeService::GetUnknown(sf::Out<u32> out) {
out.SetValue(g_unknown);
}
Result BootModeService::SetUnknown(u32 val) {
R_UNLESS(val <= 3, pm::ResultUnknown7());
g_unknown = val;
R_SUCCEED();
}
}

View File

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

View File

@@ -1,83 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pm_debug_monitor_service.hpp"
#include "impl/pm_process_manager.hpp"
#include "impl/pm_spec.hpp"
namespace ams::pm {
/* Actual command implementations. */
Result DebugMonitorService::GetModuleIdList(sf::Out<u32> out_count, const sf::OutBuffer &out_buf, u64 unused) {
R_UNLESS(out_buf.GetSize() <= std::numeric_limits<s32>::max(), pm::ResultInvalidSize());
R_RETURN(impl::GetModuleIdList(out_count.GetPointer(), out_buf.GetPointer(), out_buf.GetSize(), unused));
}
Result DebugMonitorService::GetExceptionProcessIdList(sf::Out<u32> out_count, const sf::OutArray<os::ProcessId> &out_process_ids) {
R_UNLESS(out_process_ids.GetSize() <= std::numeric_limits<s32>::max(), pm::ResultInvalidSize());
R_RETURN(impl::GetExceptionProcessIdList(out_count.GetPointer(), out_process_ids.GetPointer(), out_process_ids.GetSize()));
}
Result DebugMonitorService::StartProcess(os::ProcessId process_id) {
R_RETURN(impl::StartProcess(process_id));
}
Result DebugMonitorService::GetProcessId(sf::Out<os::ProcessId> out, ncm::ProgramId program_id) {
R_RETURN(impl::GetProcessId(out.GetPointer(), program_id));
}
Result DebugMonitorService::HookToCreateProcess(sf::OutCopyHandle out_hook, ncm::ProgramId program_id) {
os::NativeHandle event_handle;
R_TRY(impl::HookToCreateProcess(std::addressof(event_handle), program_id));
out_hook.SetValue(event_handle, false);
R_SUCCEED();
}
Result DebugMonitorService::GetApplicationProcessId(sf::Out<os::ProcessId> out) {
R_RETURN(impl::GetApplicationProcessId(out.GetPointer()));
}
Result DebugMonitorService::HookToCreateApplicationProcess(sf::OutCopyHandle out_hook) {
os::NativeHandle event_handle;
R_TRY(impl::HookToCreateApplicationProcess(std::addressof(event_handle)));
out_hook.SetValue(event_handle, false);
R_SUCCEED();
}
Result DebugMonitorService::ClearHook(u32 which) {
R_RETURN(impl::ClearHook(which));
}
Result DebugMonitorService::GetProgramId(sf::Out<ncm::ProgramId> out, os::ProcessId process_id) {
R_RETURN(impl::GetProgramId(out.GetPointer(), process_id));
}
/* Atmosphere extension commands. */
Result DebugMonitorService::AtmosphereGetProcessInfo(sf::OutCopyHandle out_process_handle, sf::Out<ncm::ProgramLocation> out_loc, sf::Out<cfg::OverrideStatus> out_status, os::ProcessId process_id) {
os::NativeHandle process_handle;
R_TRY(impl::AtmosphereGetProcessInfo(std::addressof(process_handle), out_loc.GetPointer(), out_status.GetPointer(), process_id));
out_process_handle.SetValue(process_handle, false);
R_SUCCEED();
}
Result DebugMonitorService::AtmosphereGetCurrentLimitInfo(sf::Out<s64> out_cur_val, sf::Out<s64> out_lim_val, u32 group, u32 resource) {
R_RETURN(impl::GetResourceLimitValues(out_cur_val.GetPointer(), out_lim_val.GetPointer(), static_cast<ResourceLimitGroup>(group), static_cast<svc::LimitableResource>(resource)));
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::pm {
class DebugMonitorService {
public:
/* Actual command implementations. */
Result GetModuleIdList(sf::Out<u32> out_count, const sf::OutBuffer &out_buf, u64 unused);
Result GetExceptionProcessIdList(sf::Out<u32> out_count, const sf::OutArray<os::ProcessId> &out_process_ids);
Result StartProcess(os::ProcessId process_id);
Result GetProcessId(sf::Out<os::ProcessId> out, ncm::ProgramId program_id);
Result HookToCreateProcess(sf::OutCopyHandle out_hook, ncm::ProgramId program_id);
Result GetApplicationProcessId(sf::Out<os::ProcessId> out);
Result HookToCreateApplicationProcess(sf::OutCopyHandle out_hook);
Result ClearHook(u32 which);
Result GetProgramId(sf::Out<ncm::ProgramId> out, os::ProcessId process_id);
/* Atmosphere extension commands. */
Result AtmosphereGetProcessInfo(sf::OutCopyHandle out_process_handle, sf::Out<ncm::ProgramLocation> out_loc, sf::Out<cfg::OverrideStatus> out_status, os::ProcessId process_id);
Result AtmosphereGetCurrentLimitInfo(sf::Out<s64> out_cur_val, sf::Out<s64> out_lim_val, u32 group, u32 resource);
};
static_assert(pm::impl::IsIDebugMonitorInterface<DebugMonitorService>);
}

View File

@@ -1,60 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pm_info_service.hpp"
#include "impl/pm_process_manager.hpp"
#include "impl/pm_spec.hpp"
namespace ams::pm {
/* Overrides for libstratosphere pm::info commands. */
namespace info {
Result HasLaunchedBootProgram(bool *out, ncm::ProgramId program_id) {
R_RETURN(ldr::pm::HasLaunchedBootProgram(out, program_id));
}
}
/* Actual command implementations. */
Result InformationService::GetProgramId(sf::Out<ncm::ProgramId> out, os::ProcessId process_id) {
R_RETURN(impl::GetProgramId(out.GetPointer(), process_id));
}
Result InformationService::GetAppletResourceLimitCurrentValue(sf::Out<pm::ResourceLimitValue> out) {
R_RETURN(impl::GetResourceLimitCurrentValue(out.GetPointer(), ResourceLimitGroup_Applet));
}
Result InformationService::GetAppletResourceLimitPeakValue(sf::Out<pm::ResourceLimitValue> out) {
R_RETURN(impl::GetResourceLimitPeakValue(out.GetPointer(), ResourceLimitGroup_Applet));
}
/* Atmosphere extension commands. */
Result InformationService::AtmosphereGetProcessId(sf::Out<os::ProcessId> out, ncm::ProgramId program_id) {
R_RETURN(impl::GetProcessId(out.GetPointer(), program_id));
}
Result InformationService::AtmosphereHasLaunchedBootProgram(sf::Out<bool> out, ncm::ProgramId program_id) {
R_RETURN(pm::info::HasLaunchedBootProgram(out.GetPointer(), program_id));
}
Result InformationService::AtmosphereGetProcessInfo(sf::Out<ncm::ProgramLocation> out_loc, sf::Out<cfg::OverrideStatus> out_status, os::ProcessId process_id) {
/* NOTE: We don't need to worry about closing this handle, because it's an in-process copy, not a newly allocated handle. */
os::NativeHandle dummy_handle;
R_RETURN(impl::AtmosphereGetProcessInfo(&dummy_handle, out_loc.GetPointer(), out_status.GetPointer(), process_id));
}
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::pm {
class InformationService {
public:
/* Actual command implementations. */
Result GetProgramId(sf::Out<ncm::ProgramId> out, os::ProcessId process_id);
Result GetAppletResourceLimitCurrentValue(sf::Out<pm::ResourceLimitValue> out);
Result GetAppletResourceLimitPeakValue(sf::Out<pm::ResourceLimitValue> out);
/* Atmosphere extension commands. */
Result AtmosphereGetProcessId(sf::Out<os::ProcessId> out, ncm::ProgramId program_id);
Result AtmosphereHasLaunchedBootProgram(sf::Out<bool> out, ncm::ProgramId program_id);
Result AtmosphereGetProcessInfo(sf::Out<ncm::ProgramLocation> out_loc, sf::Out<cfg::OverrideStatus> out_status, os::ProcessId process_id);
};
static_assert(pm::impl::IsIInformationInterface<InformationService>);
}

View File

@@ -1,235 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pm_boot_mode_service.hpp"
#include "pm_debug_monitor_service.hpp"
#include "pm_info_service.hpp"
#include "pm_shell_service.hpp"
#include "impl/pm_process_manager.hpp"
namespace ams {
namespace pm {
namespace {
constexpr u32 PrivilegedFileAccessHeader[0x1C / sizeof(u32)] = {0x00000001, 0x00000000, 0x80000000, 0x0000001C, 0x00000000, 0x0000001C, 0x00000000};
constexpr u32 PrivilegedFileAccessControl[0x2C / sizeof(u32)] = {0x00000001, 0x00000000, 0x80000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF};
constexpr u8 PrivilegedServiceAccessControl[] = {0x80, '*', 0x00, '*'};
constexpr size_t ProcessCountMax = 0x40;
/* This uses debugging SVCs to retrieve a process's program id. */
ncm::ProgramId GetProcessProgramId(os::ProcessId process_id) {
/* Get a debug handle. */
svc::Handle debug_handle;
R_ABORT_UNLESS(svc::DebugActiveProcess(std::addressof(debug_handle), process_id.value));
ON_SCOPE_EXIT { R_ABORT_UNLESS(svc::CloseHandle(debug_handle)); };
/* Loop until we get the event that tells us about the process. */
svc::DebugEventInfo d;
while (true) {
R_ABORT_UNLESS(svc::GetDebugEvent(std::addressof(d), debug_handle));
if (d.type == svc::DebugEvent_CreateProcess) {
return ncm::ProgramId{d.info.create_process.program_id};
}
}
}
/* This works around a bug fixed by FS in 4.0.0. */
/* Not doing so will cause KIPs with higher process IDs than 7 to be unable to use filesystem services. */
/* It also registers privileged processes with SM, so that their program ids can be known. */
void RegisterPrivilegedProcess(os::ProcessId process_id, ncm::ProgramId program_id) {
fsprUnregisterProgram(process_id.value);
fsprRegisterProgram(process_id.value, process_id.value, NcmStorageId_BuiltInSystem, PrivilegedFileAccessHeader, sizeof(PrivilegedFileAccessHeader), PrivilegedFileAccessControl, sizeof(PrivilegedFileAccessControl), 0);
sm::manager::UnregisterProcess(process_id);
sm::manager::RegisterProcess(process_id, program_id, cfg::OverrideStatus{}, PrivilegedServiceAccessControl, sizeof(PrivilegedServiceAccessControl), PrivilegedServiceAccessControl, sizeof(PrivilegedServiceAccessControl));
}
void RegisterPrivilegedProcesses() {
/* Get privileged process range. */
os::ProcessId min_priv_process_id, max_priv_process_id;
R_ABORT_UNLESS(svc::GetSystemInfo(std::addressof(min_priv_process_id.value), svc::SystemInfoType_InitialProcessIdRange, svc::InvalidHandle, svc::InitialProcessIdRangeInfo_Minimum));
R_ABORT_UNLESS(svc::GetSystemInfo(std::addressof(max_priv_process_id.value), svc::SystemInfoType_InitialProcessIdRange, svc::InvalidHandle, svc::InitialProcessIdRangeInfo_Maximum));
/* Get current process id/program id. */
const auto cur_process_id = os::GetCurrentProcessId();
const auto cur_program_id = os::GetCurrentProgramId();
/* Get list of processes, register all privileged ones. */
s32 num_pids;
os::ProcessId pids[ProcessCountMax];
R_ABORT_UNLESS(svc::GetProcessList(std::addressof(num_pids), reinterpret_cast<u64 *>(pids), ProcessCountMax));
for (s32 i = 0; i < num_pids; i++) {
if (min_priv_process_id <= pids[i] && pids[i] <= max_priv_process_id) {
RegisterPrivilegedProcess(pids[i], pids[i] == cur_process_id ? cur_program_id : GetProcessProgramId(pids[i]));
}
}
}
}
namespace {
/* pm:shell, pm:dmnt, pm:bm, pm:info. */
enum PortIndex {
PortIndex_Shell,
PortIndex_DebugMonitor,
PortIndex_BootMode,
PortIndex_Information,
PortIndex_Count,
};
using ServerOptions = sf::hipc::DefaultServerManagerOptions;
constexpr sm::ServiceName ShellServiceName = sm::ServiceName::Encode("pm:shell");
constexpr size_t ShellMaxSessions = 8; /* Official maximum is 3. */
constexpr sm::ServiceName DebugMonitorServiceName = sm::ServiceName::Encode("pm:dmnt");
constexpr size_t DebugMonitorMaxSessions = 16;
constexpr sm::ServiceName BootModeServiceName = sm::ServiceName::Encode("pm:bm");
constexpr size_t BootModeMaxSessions = 8; /* Official maximum is 4. */
constexpr sm::ServiceName InformationServiceName = sm::ServiceName::Encode("pm:info");
constexpr size_t InformationMaxSessions = 48 - (ShellMaxSessions + DebugMonitorMaxSessions + BootModeMaxSessions);
static_assert(InformationMaxSessions >= 16, "InformationMaxSessions");
constexpr size_t MaxSessions = ShellMaxSessions + DebugMonitorMaxSessions + BootModeMaxSessions + InformationMaxSessions;
static_assert(MaxSessions == 48, "MaxSessions");
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
private:
virtual ams::Result OnNeedsToAccept(int port_index, Server *server) override;
};
ServerManager g_server_manager;
/* NOTE: Nintendo only uses an unmanaged object for boot mode service, but no pm service has any class members/state, so we'll do it for all. */
sf::UnmanagedServiceObject<pm::impl::IShellInterface, pm::ShellService> g_shell_service;
sf::UnmanagedServiceObject<pm::impl::IDeprecatedShellInterface, pm::ShellService> g_deprecated_shell_service;
sf::UnmanagedServiceObject<pm::impl::IDebugMonitorInterface, pm::DebugMonitorService> g_dmnt_service;
sf::UnmanagedServiceObject<pm::impl::IDeprecatedDebugMonitorInterface, pm::DebugMonitorService> g_deprecated_dmnt_service;
sf::UnmanagedServiceObject<pm::impl::IBootModeInterface, pm::BootModeService> g_boot_mode_service;
sf::UnmanagedServiceObject<pm::impl::IInformationInterface, pm::InformationService> g_information_service;
ams::Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
switch (port_index) {
case PortIndex_Shell:
if (hos::GetVersion() >= hos::Version_5_0_0) {
R_RETURN(this->AcceptImpl(server, g_shell_service.GetShared()));
} else {
R_RETURN(this->AcceptImpl(server, g_deprecated_shell_service.GetShared()));
}
case PortIndex_DebugMonitor:
if (hos::GetVersion() >= hos::Version_5_0_0) {
R_RETURN(this->AcceptImpl(server, g_dmnt_service.GetShared()));
} else {
R_RETURN(this->AcceptImpl(server, g_deprecated_dmnt_service.GetShared()));
}
case PortIndex_BootMode:
R_RETURN(this->AcceptImpl(server, g_boot_mode_service.GetShared()));
case PortIndex_Information:
R_RETURN(this->AcceptImpl(server, g_information_service.GetShared()));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
void RegisterServices() {
/* NOTE: Extra sessions have been added to pm:bm and pm:info to facilitate access by the rest of stratosphere. */
R_ABORT_UNLESS(g_server_manager.RegisterServer(PortIndex_Shell, ShellServiceName, ShellMaxSessions));
R_ABORT_UNLESS(g_server_manager.RegisterServer(PortIndex_DebugMonitor, DebugMonitorServiceName, DebugMonitorMaxSessions));
R_ABORT_UNLESS(g_server_manager.RegisterServer(PortIndex_BootMode, BootModeServiceName, BootModeMaxSessions));
R_ABORT_UNLESS(g_server_manager.RegisterServer(PortIndex_Information, InformationServiceName, InformationMaxSessions));
}
void LoopProcess() {
g_server_manager.LoopProcess();
}
}
}
namespace hos {
void InitializeVersionInternal(bool allow_approximate);
}
namespace init {
void InitializeSystemModule() {
/* Initialize our connection to sm. */
R_ABORT_UNLESS(sm::Initialize());
/* Initialize manager interfaces for fs and sm. */
R_ABORT_UNLESS(fsprInitialize());
R_ABORT_UNLESS(smManagerInitialize());
/* Work around a bug with process permissions on < 4.0.0. */
/* This registers all initial processes explicitly with both fs and sm. */
pm::RegisterPrivilegedProcesses();
/* Use our manager extension to tell SM that the FS bug has been worked around. */
R_ABORT_UNLESS(sm::manager::EndInitialDefers());
/* Wait for the true hos version to be available. */
hos::InitializeVersionInternal(false);
/* Now that the true hos version is available, we should once more end defers (alerting sm to the available hos version). */
R_ABORT_UNLESS(sm::manager::EndInitialDefers());
/* Initialize remaining services we need. */
R_ABORT_UNLESS(ldrPmInitialize());
spl::Initialize();
/* Verify that we can sanely execute. */
ams::CheckApiVersion();
}
void FinalizeSystemModule() { /* ... */ }
void Startup() { /* ... */ }
}
void NORETURN Exit(int rc) {
AMS_UNUSED(rc);
AMS_ABORT("Exit called by immortal process");
}
void Main() {
/* Set thread name. */
os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(pm, Main));
AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(pm, Main));
/* Initialize process manager implementation. */
R_ABORT_UNLESS(pm::impl::InitializeProcessManager());
/* Create Services. */
pm::RegisterServices();
/* Loop forever, servicing our services. */
pm::LoopProcess();
/* This can never be reached. */
AMS_ASSUME(false);
}
}

View File

@@ -1,91 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pm_shell_service.hpp"
#include "impl/pm_process_manager.hpp"
#include "impl/pm_spec.hpp"
namespace ams::pm {
/* Overrides for libstratosphere pm::shell commands. */
namespace shell {
Result LaunchProgram(os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 launch_flags) {
R_RETURN(impl::LaunchProgram(out_process_id, loc, launch_flags));
}
}
/* Service command implementations. */
Result ShellService::LaunchProgram(sf::Out<os::ProcessId> out_process_id, const ncm::ProgramLocation &loc, u32 flags) {
R_RETURN(pm::shell::LaunchProgram(out_process_id.GetPointer(), loc, flags));
}
Result ShellService::TerminateProcess(os::ProcessId process_id) {
R_RETURN(impl::TerminateProcess(process_id));
}
Result ShellService::TerminateProgram(ncm::ProgramId program_id) {
R_RETURN(impl::TerminateProgram(program_id));
}
void ShellService::GetProcessEventHandle(sf::OutCopyHandle out) {
os::NativeHandle event_handle;
R_ABORT_UNLESS(impl::GetProcessEventHandle(std::addressof(event_handle)));
out.SetValue(event_handle, false);
}
void ShellService::GetProcessEventInfo(sf::Out<ProcessEventInfo> out) {
R_ABORT_UNLESS(impl::GetProcessEventInfo(out.GetPointer()));
}
Result ShellService::CleanupProcess(os::ProcessId process_id) {
R_RETURN(impl::CleanupProcess(process_id));
}
Result ShellService::ClearExceptionOccurred(os::ProcessId process_id) {
R_RETURN(impl::ClearExceptionOccurred(process_id));
}
void ShellService::NotifyBootFinished() {
R_ABORT_UNLESS(impl::NotifyBootFinished());
}
Result ShellService::GetApplicationProcessIdForShell(sf::Out<os::ProcessId> out) {
R_RETURN(impl::GetApplicationProcessId(out.GetPointer()));
}
Result ShellService::BoostSystemMemoryResourceLimit(u64 boost_size) {
R_RETURN(impl::BoostSystemMemoryResourceLimit(boost_size));
}
Result ShellService::BoostApplicationThreadResourceLimit() {
R_RETURN(impl::BoostApplicationThreadResourceLimit());
}
void ShellService::GetBootFinishedEventHandle(sf::OutCopyHandle out) {
os::NativeHandle event_handle;
R_ABORT_UNLESS(impl::GetBootFinishedEventHandle(std::addressof(event_handle)));
out.SetValue(event_handle, false);
}
Result ShellService::BoostSystemThreadResourceLimit() {
R_RETURN(impl::BoostSystemThreadResourceLimit());
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::pm {
class ShellService {
public:
/* Actual command implementations. */
Result LaunchProgram(sf::Out<os::ProcessId> out_process_id, const ncm::ProgramLocation &loc, u32 flags);
Result TerminateProcess(os::ProcessId process_id);
Result TerminateProgram(ncm::ProgramId program_id);
void GetProcessEventHandle(sf::OutCopyHandle out);
void GetProcessEventInfo(sf::Out<ProcessEventInfo> out);
Result CleanupProcess(os::ProcessId process_id);
Result ClearExceptionOccurred(os::ProcessId process_id);
void NotifyBootFinished();
Result GetApplicationProcessIdForShell(sf::Out<os::ProcessId> out);
Result BoostSystemMemoryResourceLimit(u64 boost_size);
Result BoostApplicationThreadResourceLimit();
void GetBootFinishedEventHandle(sf::OutCopyHandle out);
Result BoostSystemThreadResourceLimit();
};
static_assert(pm::impl::IsIShellInterface<ShellService>);
}