pm: update to reflect 17.0.0 internal design changes

This commit is contained in:
Michael Scire
2023-10-26 14:44:32 -07:00
parent 274f6b63f2
commit 0c3afff4d3
13 changed files with 428 additions and 332 deletions

View File

@@ -15,9 +15,9 @@
*/
#include <stratosphere.hpp>
#include "pm_process_manager.hpp"
#include "pm_resource_manager.hpp"
#include "pm_process_tracker.hpp"
#include "pm_process_info.hpp"
#include "pm_spec.hpp"
namespace ams::pm::impl {
@@ -29,13 +29,7 @@ namespace ams::pm::impl {
HookType_Application = (1 << 1),
};
struct LaunchProcessArgs {
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))
#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();
@@ -70,115 +64,26 @@ namespace ams::pm::impl {
return launch_flags & GET_FLAG_MASK(DisableAslr);
}
#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;
}
};
#undef GET_FLAG_MASK
/* Process Tracking globals. */
void ProcessTrackingMain(void *);
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;
constinit ProcessTracker g_process_tracker;
alignas(os::ThreadStackAlignment) constinit u8 g_process_track_thread_stack[8_KB];
/* Global events. */
constinit os::SystemEventType g_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_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. */
constinit std::atomic<ncm::ProgramId> g_program_id_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. */
void ProcessTrackingMain(void *) {
/* This is the main loop of the process tracking thread. */
/* Setup multi wait/holders. */
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)));
}
}
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) {
@@ -195,7 +100,7 @@ namespace ams::pm::impl {
}
bool HasApplicationProcess() {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
for (auto &process : *list) {
if (process.IsApplication()) {
@@ -212,19 +117,14 @@ namespace ams::pm::impl {
R_SUCCEED();
}
void CleanupProcessInfo(ProcessListAccessor &list, ProcessInfo *process_info) {
/* Remove the process from the list. */
list->Remove(process_info);
Result LaunchProgramImpl(ProcessInfo **out_process_info, os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 flags) {
/* Set the output to nullptr, if we fail. */
*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. */
ldr::ProgramInfo program_info;
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 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());
/* 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. */
os::NativeHandle process_handle;
ldr::PinId pin_id;
{
/* 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. */
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)); } };
/* Ensure resources are available. */
resource::WaitResourceAvailable(std::addressof(program_info));
WaitResourceAvailable(std::addressof(program_info));
/* 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. */
os::ProcessId process_id = os::GetProcessId(process_handle);
/* 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);
/* 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);
process_info->LinkToMultiWait(multi_wait);
}
/* Prevent resource leakage if register fails. */
ON_RESULT_FAILURE {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
process_info->Cleanup();
CleanupProcessInfo(list, process_info);
};
@@ -296,166 +195,73 @@ namespace ams::pm::impl {
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>(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(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(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, 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(args.flags) && allow_debug) {
if (ShouldSignalOnStart(flags) && allow_debug) {
process_info->SetSignalOnStart();
}
if (ShouldSignalOnExit(args.flags)) {
if (ShouldSignalOnExit(flags)) {
process_info->SetSignalOnExit();
}
if (ShouldSignalOnDebugEvent(args.flags) && allow_debug) {
if (ShouldSignalOnDebugEvent(flags) && allow_debug) {
process_info->SetSignalOnDebugEvent();
}
/* 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));
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(args.flags)) {
} else if (!ShouldStartSuspended(flags)) {
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();
}
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. */
Result InitializeProcessManager() {
/* Create events. */
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_process_event), os::EventClearMode_AutoClear, true));
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));
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_boot_finished_event), os::EventClearMode_AutoClear, true));
CreateProcessEvent();
CreateDebuggerEvent();
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_boot_finished_event), os::EventClearMode_AutoClear, true));
/* Initialize resource limits. */
R_TRY(resource::InitializeResourceManager());
R_TRY(InitializeSpec());
/* Create thread. */
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)));
os::SetThreadNamePointer(std::addressof(g_process_track_thread), AMS_GET_SYSTEM_THREAD_NAME(pm, ProcessTrack));
/* Initialize the process tracker. */
g_process_tracker.Initialize(g_process_track_thread_stack, sizeof(g_process_track_thread_stack));
/* Start thread. */
os::StartThread(std::addressof(g_process_track_thread));
/* 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) {
/* Ensure we only try to launch one program at a time. */
std::scoped_lock lk(g_launch_program_lock);
/* Launch the program. */
ProcessInfo *process_info = nullptr;
R_TRY(LaunchProgramImpl(std::addressof(process_info), out_process_id, loc, flags));
/* Set global arguments, signal, wait. */
g_process_launch_args = {
.out_process_id = out_process_id,
.location = loc,
.flags = flags,
};
g_process_launch_start_event.Signal();
g_process_launch_finish_event.Wait();
R_RETURN(g_process_launch_result);
/* Register the process info with the tracker. */
g_process_tracker.QueueEntry(process_info);
R_SUCCEED();
}
Result StartProcess(os::ProcessId process_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
@@ -467,7 +273,7 @@ namespace ams::pm::impl {
}
Result TerminateProcess(os::ProcessId process_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
@@ -476,7 +282,7 @@ namespace ams::pm::impl {
}
Result TerminateProgram(ncm::ProgramId program_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(program_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
@@ -484,15 +290,10 @@ namespace ams::pm::impl {
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) {
/* Check for event from current process. */
{
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
for (auto &process : *list) {
@@ -528,14 +329,14 @@ namespace ams::pm::impl {
/* Check for event from exited process. */
if (hos::GetVersion() >= hos::Version_5_0_0) {
ProcessListAccessor dead_list(g_dead_process_list);
auto exit_list = GetExitList();
if (!dead_list->empty()) {
auto &process_info = dead_list->front();
out->event = GetProcessEventValue(ProcessEvent::Exited);
if (!exit_list->empty()) {
auto &process_info = exit_list->front();
out->event = GetProcessEventValue(ProcessEvent::Exited);
out->process_id = process_info.GetProcessId();
CleanupProcessInfo(dead_list, std::addressof(process_info));
CleanupProcessInfo(exit_list, std::addressof(process_info));
R_SUCCEED();
}
}
@@ -546,7 +347,7 @@ namespace ams::pm::impl {
}
Result CleanupProcess(os::ProcessId process_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
@@ -557,7 +358,7 @@ namespace ams::pm::impl {
}
Result ClearExceptionOccurred(os::ProcessId process_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(process_id);
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) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
size_t count = 0;
@@ -596,7 +397,7 @@ namespace ams::pm::impl {
}
Result GetProcessId(os::ProcessId *out, ncm::ProgramId program_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(program_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
@@ -606,7 +407,7 @@ namespace ams::pm::impl {
}
Result GetProgramId(ncm::ProgramId *out, os::ProcessId process_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
@@ -616,7 +417,7 @@ namespace ams::pm::impl {
}
Result GetApplicationProcessId(os::ProcessId *out_process_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
for (auto &process : *list) {
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) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
@@ -706,33 +507,4 @@ namespace ams::pm::impl {
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));
}
}