diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index 3f6df380c..d6eacecce 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit 3f6df380c8becb4ba0a013cf0533364163a741d8 +Subproject commit d6eacecce97d93b8261a9def8f69385f6a231dc9 diff --git a/stratosphere/pm/Makefile b/stratosphere/pm/Makefile index 41eae74cb..9db18530e 100644 --- a/stratosphere/pm/Makefile +++ b/stratosphere/pm/Makefile @@ -26,7 +26,7 @@ endif #--------------------------------------------------------------------------------- TARGET := $(notdir $(CURDIR)) BUILD := build -SOURCES := source +SOURCES := source source/impl source/boot2 DATA := data INCLUDES := include ../../common/include EXEFS_SRC := exefs_src diff --git a/stratosphere/pm/source/boot2/boot2_api.cpp b/stratosphere/pm/source/boot2/boot2_api.cpp new file mode 100644 index 000000000..6c00d4833 --- /dev/null +++ b/stratosphere/pm/source/boot2/boot2_api.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include "boot2_api.hpp" + +namespace sts::boot2 { + + namespace { + + /* Launch lists. */ + + /* psc, bus, pcv is the minimal set of required titles to get SD card. */ + /* bus depends on pcie, and pcv depends on settings. */ + constexpr ncm::TitleId PreSdCardLaunchPrograms[] = { + ncm::TitleId{TitleId_Psc}, /* psc */ + ncm::TitleId{TitleId_Pcie}, /* pcie */ + ncm::TitleId{TitleId_Bus}, /* bus */ + ncm::TitleId{TitleId_Settings}, /* settings */ + ncm::TitleId{TitleId_Pcv}, /* pcv */ + }; + constexpr size_t NumPreSdCardLaunchPrograms = sizeof(PreSdCardLaunchPrograms) / sizeof(PreSdCardLaunchPrograms[0]); + + constexpr ncm::TitleId AdditionalLaunchPrograms[] = { + ncm::TitleId{TitleId_Usb}, /* usb */ + ncm::TitleId{TitleId_Tma}, /* tma */ + ncm::TitleId{TitleId_Am}, /* am */ + ncm::TitleId{TitleId_NvServices}, /* nvservices */ + ncm::TitleId{TitleId_NvnFlinger}, /* nvnflinger */ + ncm::TitleId{TitleId_Vi}, /* vi */ + ncm::TitleId{TitleId_Ns}, /* ns */ + ncm::TitleId{TitleId_LogManager}, /* lm */ + ncm::TitleId{TitleId_Ppc}, /* ppc */ + ncm::TitleId{TitleId_Ptm}, /* ptm */ + ncm::TitleId{TitleId_Hid}, /* hid */ + ncm::TitleId{TitleId_Audio}, /* audio */ + ncm::TitleId{TitleId_Lbl}, /* lbl */ + ncm::TitleId{TitleId_Wlan}, /* wlan */ + ncm::TitleId{TitleId_Bluetooth}, /* bluetooth */ + ncm::TitleId{TitleId_BsdSockets}, /* bsdsockets */ + ncm::TitleId{TitleId_Nifm}, /* nifm */ + ncm::TitleId{TitleId_Ldn}, /* ldn */ + ncm::TitleId{TitleId_Account}, /* account */ + ncm::TitleId{TitleId_Friends}, /* friends */ + ncm::TitleId{TitleId_Nfc}, /* nfc */ + ncm::TitleId{TitleId_JpegDec}, /* jpegdec */ + ncm::TitleId{TitleId_CapSrv}, /* capsrv */ + ncm::TitleId{TitleId_Ssl}, /* ssl */ + ncm::TitleId{TitleId_Nim}, /* nim */ + ncm::TitleId{TitleId_Bcat}, /* bcat */ + ncm::TitleId{TitleId_Erpt}, /* erpt */ + ncm::TitleId{TitleId_Es}, /* es */ + ncm::TitleId{TitleId_Pctl}, /* pctl */ + ncm::TitleId{TitleId_Btm}, /* btm */ + ncm::TitleId{TitleId_Eupld}, /* eupld */ + ncm::TitleId{TitleId_Glue}, /* glue */ + /* ncm::TitleId{TitleId_Eclct}, */ /* eclct */ /* Skip launching error collection in Atmosphere to lessen telemetry. */ + ncm::TitleId{TitleId_Npns}, /* npns */ + ncm::TitleId{TitleId_Fatal}, /* fatal */ + ncm::TitleId{TitleId_Ro}, /* ro */ + ncm::TitleId{TitleId_Profiler}, /* profiler */ + ncm::TitleId{TitleId_Sdb}, /* sdb */ + ncm::TitleId{TitleId_Migration}, /* migration */ + ncm::TitleId{TitleId_Grc}, /* grc */ + ncm::TitleId{TitleId_Olsc}, /* olsc */ + }; + constexpr size_t NumAdditionalLaunchPrograms = sizeof(AdditionalLaunchPrograms) / sizeof(AdditionalLaunchPrograms[0]); + + constexpr ncm::TitleId AdditionalMaintenanceLaunchPrograms[] = { + ncm::TitleId{TitleId_Usb}, /* usb */ + ncm::TitleId{TitleId_Tma}, /* tma */ + ncm::TitleId{TitleId_Am}, /* am */ + ncm::TitleId{TitleId_NvServices}, /* nvservices */ + ncm::TitleId{TitleId_NvnFlinger}, /* nvnflinger */ + ncm::TitleId{TitleId_Vi}, /* vi */ + ncm::TitleId{TitleId_Ns}, /* ns */ + ncm::TitleId{TitleId_LogManager}, /* lm */ + ncm::TitleId{TitleId_Ppc}, /* ppc */ + ncm::TitleId{TitleId_Ptm}, /* ptm */ + ncm::TitleId{TitleId_Hid}, /* hid */ + ncm::TitleId{TitleId_Audio}, /* audio */ + ncm::TitleId{TitleId_Lbl}, /* lbl */ + ncm::TitleId{TitleId_Wlan}, /* wlan */ + ncm::TitleId{TitleId_Bluetooth}, /* bluetooth */ + ncm::TitleId{TitleId_BsdSockets}, /* bsdsockets */ + ncm::TitleId{TitleId_Nifm}, /* nifm */ + ncm::TitleId{TitleId_Ldn}, /* ldn */ + ncm::TitleId{TitleId_Account}, /* account */ + ncm::TitleId{TitleId_Nfc}, /* nfc */ + ncm::TitleId{TitleId_JpegDec}, /* jpegdec */ + ncm::TitleId{TitleId_CapSrv}, /* capsrv */ + ncm::TitleId{TitleId_Ssl}, /* ssl */ + ncm::TitleId{TitleId_Nim}, /* nim */ + ncm::TitleId{TitleId_Erpt}, /* erpt */ + ncm::TitleId{TitleId_Es}, /* es */ + ncm::TitleId{TitleId_Pctl}, /* pctl */ + ncm::TitleId{TitleId_Btm}, /* btm */ + ncm::TitleId{TitleId_Glue}, /* glue */ + /* ncm::TitleId{TitleId_Eclct}, */ /* eclct */ /* Skip launching error collection in Atmosphere to lessen telemetry. */ + ncm::TitleId{TitleId_Fatal}, /* fatal */ + ncm::TitleId{TitleId_Ro}, /* ro */ + ncm::TitleId{TitleId_Profiler}, /* profiler */ + ncm::TitleId{TitleId_Sdb}, /* sdb */ + ncm::TitleId{TitleId_Migration}, /* migration */ + ncm::TitleId{TitleId_Grc}, /* grc */ + ncm::TitleId{TitleId_Olsc}, /* olsc */ + }; + constexpr size_t NumAdditionalMaintenanceLaunchPrograms = sizeof(AdditionalMaintenanceLaunchPrograms) / sizeof(AdditionalMaintenanceLaunchPrograms[0]); + + /* Helpers. */ + inline bool IsHexadecimal(const char *str) { + while (*str) { + if (!std::isxdigit(static_cast(*(str++)))) { + return false; + } + } + return true; + } + + void LaunchTitle(u64 *out_process_id, const ncm::TitleLocation &loc, u32 launch_flags) { + u64 process_id = 0; + + /* Don't launch a title twice during boot2. */ + if (pm::info::HasLaunchedTitle(loc.title_id)) { + return; + } + + switch (pm::shell::LaunchTitle(&process_id, loc, launch_flags)) { + case ResultKernelResourceExhausted: + /* Out of resource! */ + std::abort(); + case ResultKernelOutOfMemory: + /* Out of memory! */ + std::abort(); + case ResultKernelLimitReached: + /* Limit Reached! */ + std::abort(); + default: + /* We don't care about other issues. */ + break; + } + + if (out_process_id) { + *out_process_id = process_id; + } + } + + void LaunchList(const ncm::TitleId *launch_list, size_t num_entries) { + for (size_t i = 0; i < num_entries; i++) { + LaunchTitle(nullptr, ncm::MakeTitleLocation(launch_list[i], ncm::StorageId::NandSystem), 0); + } + } + + bool GetGpioPadLow(GpioPadName pad) { + GpioPadSession button; + if (R_FAILED(gpioOpenSession(&button, pad))) { + return false; + } + + /* Ensure we close even on early return. */ + ON_SCOPE_EXIT { gpioPadClose(&button); }; + + /* Set direction input. */ + gpioPadSetDirection(&button, GpioDirection_Input); + + GpioValue val; + return R_SUCCEEDED(gpioPadGetValue(&button, &val)) && val == GpioValue_Low; + } + + bool IsMaintenanceMode() { + /* Contact set:sys, retrieve boot!force_maintenance. */ + DoWithSmSession([&]() { + R_ASSERT(setsysInitialize()); + }); + { + ON_SCOPE_EXIT { setsysExit(); }; + + u8 force_maintenance = 1; + setsysGetSettingsItemValue("boot", "force_maintenance", &force_maintenance, sizeof(force_maintenance)); + if (force_maintenance != 0) { + return true; + } + } + + /* Contact GPIO, read plus/minus buttons. */ + DoWithSmSession([&]() { + R_ASSERT(gpioInitialize()); + }); + { + ON_SCOPE_EXIT { gpioExit(); }; + + return GetGpioPadLow(GpioPadName_ButtonVolUp) && GetGpioPadLow(GpioPadName_ButtonVolDown); + } + } + + void WaitForMitm(const char *service) { + const auto name = sts::sm::ServiceName::Encode(service); + + while (true) { + bool mitm_installed = false; + R_ASSERT(sts::sm::mitm::HasMitm(&mitm_installed, name)); + if (mitm_installed) { + break; + } + svcSleepThread(1'000'000ull); + } + } + + void LaunchFlaggedProgramsFromSdCard() { + /* Allow for user-customizable programs. */ + DIR *titles_dir = opendir("sdmc:/atmosphere/titles"); + struct dirent *ent; + if (titles_dir != NULL) { + while ((ent = readdir(titles_dir)) != NULL) { + if (strlen(ent->d_name) == 2 * sizeof(u64) && IsHexadecimal(ent->d_name)) { + ncm::TitleId title_id{strtoul(ent->d_name, NULL, 16)}; + if (pm::info::HasLaunchedTitle(title_id)) { + return; + } + char title_path[FS_MAX_PATH]; + std::snprintf(title_path, sizeof(title_path), "sdmc:/atmosphere/titles/%s/flags/boot2.flag", ent->d_name); + FILE *f_flag = fopen(title_path, "rb"); + if (f_flag != NULL) { + fclose(f_flag); + LaunchTitle(nullptr, ncm::MakeTitleLocation(title_id, ncm::StorageId::None), 0); + } + } + } + closedir(titles_dir); + } + } + + } + + /* Boot2 API. */ + void LaunchBootPrograms() { + /* Wait until fs.mitm has installed itself. We want this to happen as early as possible. */ + WaitForMitm("fsp-srv"); + + /* Launch programs required to mount the SD card. */ + LaunchList(PreSdCardLaunchPrograms, NumPreSdCardLaunchPrograms); + + /* At this point, the SD card can be mounted. */ + cfg::WaitSdCardInitialized(); + R_ASSERT(fsdevMountSdmc()); + + /* Find out whether we are maintenance mode. */ + const bool maintenance = IsMaintenanceMode(); + if (maintenance) { + pm::bm::SetMaintenanceBoot(); + } + + /* Wait for other atmosphere mitm modules to initialize. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) { + WaitForMitm("bpc"); + } else { + WaitForMitm("bpc:c"); + } + + /* Launch Atmosphere dmnt, using FsStorageId_None to force SD card boot. */ + LaunchTitle(nullptr, ncm::MakeTitleLocation(ncm::TitleId{TitleId_Dmnt}, ncm::StorageId::None), 0); + + /* Launch additional programs. */ + if (maintenance) { + LaunchList(AdditionalMaintenanceLaunchPrograms, NumAdditionalMaintenanceLaunchPrograms); + } else { + LaunchList(AdditionalLaunchPrograms, NumAdditionalLaunchPrograms); + } + + /* Launch user programs off of the SD. */ + LaunchFlaggedProgramsFromSdCard(); + + /* We no longer need the SD card. */ + fsdevUnmountAll(); + } + +} diff --git a/stratosphere/pm/source/pm_process_track.hpp b/stratosphere/pm/source/boot2/boot2_api.hpp similarity index 86% rename from stratosphere/pm/source/pm_process_track.hpp rename to stratosphere/pm/source/boot2/boot2_api.hpp index c781a8cfe..daa1f21b1 100644 --- a/stratosphere/pm/source/pm_process_track.hpp +++ b/stratosphere/pm/source/boot2/boot2_api.hpp @@ -16,8 +16,11 @@ #pragma once #include +#include -class ProcessTracking { - public: - static void MainLoop(void *arg); -}; \ No newline at end of file +namespace sts::boot2 { + + /* Boot2 API. */ + void LaunchBootPrograms(); + +} diff --git a/stratosphere/pm/source/impl/pm_process_info.cpp b/stratosphere/pm/source/impl/pm_process_info.cpp new file mode 100644 index 000000000..4e53c0924 --- /dev/null +++ b/stratosphere/pm/source/impl/pm_process_info.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "pm_process_info.hpp" + +namespace sts::pm::impl { + + ProcessInfo::ProcessInfo(Handle h, u64 pid, ldr::PinId pin, const ncm::TitleLocation &l) : process_id(pid), pin_id(pin), loc(l), handle(h), state(ProcessState_Created), flags(0) { + /* ... */ + } + + ProcessInfo::~ProcessInfo() { + this->Cleanup(); + } + + void ProcessInfo::Cleanup() { + if (this->handle != INVALID_HANDLE) { + /* Unregister the process. */ + fsprUnregisterProgram(this->process_id); + sm::manager::UnregisterProcess(this->process_id); + ldr::pm::UnpinTitle(this->pin_id); + + /* Close the process's handle. */ + svcCloseHandle(this->handle); + this->handle = INVALID_HANDLE; + } + } + +} diff --git a/stratosphere/pm/source/impl/pm_process_info.hpp b/stratosphere/pm/source/impl/pm_process_info.hpp new file mode 100644 index 000000000..7a0e98198 --- /dev/null +++ b/stratosphere/pm/source/impl/pm_process_info.hpp @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include + +#include "pm_process_manager.hpp" + +namespace sts::pm::impl { + + class ProcessInfo { + NON_COPYABLE(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), + }; + private: + const u64 process_id; + const ldr::PinId pin_id; + const ncm::TitleLocation loc; + Handle handle; + ProcessState state; + u32 flags; + private: + void SetFlag(Flag flag) { + this->flags |= flag; + } + + void ClearFlag(Flag flag) { + this->flags &= ~flag; + } + + bool HasFlag(Flag flag) const { + return (this->flags & flag); + } + public: + ProcessInfo(Handle h, u64 pid, ldr::PinId pin, const ncm::TitleLocation &l); + ~ProcessInfo(); + void Cleanup(); + + Handle GetHandle() const { + return this->handle; + } + + u64 GetProcessId() const { + return this->process_id; + } + + ldr::PinId GetPinId() const { + return this->pin_id; + } + + const ncm::TitleLocation &GetTitleLocation() { + return this->loc; + } + + ProcessState GetState() const { + return this->state; + } + + void SetState(ProcessState state) { + this->state = state; + } + + void SetSignalOnExit() { + this->SetFlag(Flag_SignalOnExit); + } + + void SetExceptionOccurred() { + this->SetFlag(Flag_ExceptionOccurred); + this->SetFlag(Flag_ExceptionWaitingAttach); + } + + void ClearExceptionOccurred() { + this->ClearFlag(Flag_ExceptionOccurred); + } + + void ClearExceptionWaitingAttach() { + this->ClearFlag(Flag_ExceptionWaitingAttach); + } + + void SetSignalOnDebugEvent() { + this->SetFlag(Flag_SignalOnDebugEvent); + } + + void SetSuspendedStateChanged() { + this->SetFlag(Flag_SuspendedStateChanged); + } + + void ClearSuspendedStateChanged() { + this->ClearFlag(Flag_SuspendedStateChanged); + } + + void SetSuspended() { + this->SetFlag(Flag_Suspended); + } + + void ClearSuspended() { + this->ClearFlag(Flag_Suspended); + } + + void SetApplication() { + this->SetFlag(Flag_Application); + } + + void SetSignalOnStart() { + this->SetFlag(Flag_SignalOnStart); + } + + void ClearSignalOnStart() { + this->ClearFlag(Flag_SignalOnStart); + } + + void SetStartedStateChanged() { + this->SetFlag(Flag_StartedStateChanged); + } + + void ClearStartedStateChanged() { + this->ClearFlag(Flag_StartedStateChanged); + } + + bool HasStarted() const { + return this->state != ProcessState_Created && this->state != ProcessState_CreatedAttached; + } + + bool HasExited() const { + return this->state == ProcessState_Exited; + } + + bool ShouldSignalOnExit() const { + return this->HasFlag(Flag_SignalOnExit); + } + + bool HasExceptionOccurred() const { + return this->HasFlag(Flag_ExceptionOccurred); + } + + bool HasExceptionWaitingAttach() const { + return this->HasFlag(Flag_ExceptionWaitingAttach); + } + + bool ShouldSignalOnDebugEvent() const { + return this->HasFlag(Flag_SignalOnDebugEvent); + } + + bool ShouldSignalOnStart() const { + return this->HasFlag(Flag_SignalOnStart); + } + + bool HasSuspendedStateChanged() const { + return this->HasFlag(Flag_SuspendedStateChanged); + } + + bool IsSuspended() const { + return this->HasFlag(Flag_Suspended); + } + + bool IsApplication() const { + return this->HasFlag(Flag_Application); + } + + bool HasStartedStateChanged() const { + return this->HasFlag(Flag_StartedStateChanged); + } + + }; + + Result OnProcessSignaled(std::shared_ptr process_info); + + class ProcessInfoWaiter final : public IWaitable { + private: + std::shared_ptr process_info; + public: + ProcessInfoWaiter(std::shared_ptr p) : process_info(p) { /* ... */ } + + /* IWaitable */ + Handle GetHandle() override { + return this->process_info->GetHandle(); + } + + Result HandleSignaled(u64 timeout) override { + return OnProcessSignaled(this->process_info); + } + }; + + class ProcessList final { + private: + HosMutex lock; + std::vector> processes; + public: + void Lock() { + this->lock.Lock(); + } + + void Unlock() { + this->lock.Unlock(); + } + + size_t GetSize() const { + return this->processes.size(); + } + + std::shared_ptr Pop() { + auto front = this->processes[0]; + this->processes.erase(this->processes.begin()); + return front; + } + + void Add(std::shared_ptr process_info) { + this->processes.push_back(process_info); + } + + void Remove(u64 process_id) { + for (size_t i = 0; i < this->processes.size(); i++) { + if (this->processes[i]->GetProcessId() == process_id) { + this->processes.erase(this->processes.begin() + i); + break; + } + } + } + + std::shared_ptr Find(u64 process_id) { + for (size_t i = 0; i < this->processes.size(); i++) { + if (this->processes[i]->GetProcessId() == process_id) { + return this->processes[i]; + } + } + return nullptr; + } + + std::shared_ptr Find(ncm::TitleId title_id) { + for (size_t i = 0; i < this->processes.size(); i++) { + if (this->processes[i]->GetTitleLocation().title_id == title_id) { + return this->processes[i]; + } + } + return nullptr; + } + + std::shared_ptr operator[](int i) { + return this->processes[i]; + } + + const std::shared_ptr operator[](int i) const { + return this->processes[i]; + } + }; + + class ProcessListAccessor final { + private: + ProcessList &list; + public: + explicit ProcessListAccessor(ProcessList &l) : list(l) { + this->list.Lock(); + } + + ~ProcessListAccessor() { + this->list.Unlock(); + } + + ProcessList *operator->() { + return &this->list; + } + + const ProcessList *operator->() const { + return &this->list; + } + + ProcessList &operator*() { + return this->list; + } + + const ProcessList &operator*() const { + return this->list; + } + + std::shared_ptr operator[](int i) { + return this->list[i]; + } + + const std::shared_ptr operator[](int i) const { + return this->list[i]; + } + }; + +} diff --git a/stratosphere/pm/source/impl/pm_process_manager.cpp b/stratosphere/pm/source/impl/pm_process_manager.cpp new file mode 100644 index 000000000..1a7373a55 --- /dev/null +++ b/stratosphere/pm/source/impl/pm_process_manager.cpp @@ -0,0 +1,679 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "pm_process_manager.hpp" +#include "pm_resource_manager.hpp" + +#include "pm_process_info.hpp" + +#include "../boot2/boot2_api.hpp" + +namespace sts::pm::impl { + + namespace { + + /* Types. */ + enum HookType { + HookType_TitleId = (1 << 0), + HookType_Application = (1 << 1), + }; + + struct LaunchProcessArgs { + u64 *out_process_id; + ncm::TitleLocation location; + u32 flags; + }; + + enum LaunchFlags { + LaunchFlags_None = 0, + LaunchFlags_SignalOnExit = (1 << 0), + LaunchFlags_SignalOnStart = (1 << 1), + LaunchFlags_SignalOnException = (1 << 2), + LaunchFlags_SignalOnDebugEvent = (1 << 3), + LaunchFlags_StartSuspended = (1 << 4), + LaunchFlags_DisableAslr = (1 << 5), + }; + + enum LaunchFlagsDeprecated { + LaunchFlagsDeprecated_None = 0, + LaunchFlagsDeprecated_SignalOnExit = (1 << 0), + LaunchFlagsDeprecated_StartSuspended = (1 << 1), + LaunchFlagsDeprecated_SignalOnException = (1 << 2), + LaunchFlagsDeprecated_DisableAslr = (1 << 3), + LaunchFlagsDeprecated_SignalOnDebugEvent = (1 << 4), + LaunchFlagsDeprecated_SignalOnStart = (1 << 5), + }; + +#define GET_FLAG_MASK(flag) (firmware_version >= FirmwareVersion_500 ? static_cast(LaunchFlags_##flag) : static_cast(LaunchFlagsDeprecated_##flag)) + + inline bool ShouldSignalOnExit(u32 launch_flags) { + const auto firmware_version = GetRuntimeFirmwareVersion(); + return launch_flags & GET_FLAG_MASK(SignalOnExit); + } + + inline bool ShouldSignalOnStart(u32 launch_flags) { + const auto firmware_version = GetRuntimeFirmwareVersion(); + if (firmware_version < FirmwareVersion_200) { + return false; + } + return launch_flags & GET_FLAG_MASK(SignalOnStart); + } + + inline bool ShouldSignalOnException(u32 launch_flags) { + const auto firmware_version = GetRuntimeFirmwareVersion(); + return launch_flags & GET_FLAG_MASK(SignalOnException); + } + + inline bool ShouldSignalOnDebugEvent(u32 launch_flags) { + const auto firmware_version = GetRuntimeFirmwareVersion(); + return launch_flags & GET_FLAG_MASK(SignalOnDebugEvent); + } + + inline bool ShouldStartSuspended(u32 launch_flags) { + const auto firmware_version = GetRuntimeFirmwareVersion(); + return launch_flags & GET_FLAG_MASK(StartSuspended); + } + + inline bool ShouldDisableAslr(u32 launch_flags) { + const auto firmware_version = GetRuntimeFirmwareVersion(); + return launch_flags & GET_FLAG_MASK(DisableAslr); + } + +#undef GET_FLAG_MASK + + enum class ProcessEvent { + None = 0, + Exited = 1, + Started = 2, + Exception = 3, + DebugRunning = 4, + DebugSuspended = 5, + }; + + enum class ProcessEventDeprecated { + None = 0, + Exception = 1, + Exited = 2, + DebugRunning = 3, + DebugSuspended = 4, + Started = 5, + }; + + inline u32 GetProcessEventValue(ProcessEvent event) { + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + return static_cast(event); + } + switch (event) { + case ProcessEvent::None: + return static_cast(ProcessEventDeprecated::None); + case ProcessEvent::Exited: + return static_cast(ProcessEventDeprecated::Exited); + case ProcessEvent::Started: + return static_cast(ProcessEventDeprecated::Started); + case ProcessEvent::Exception: + return static_cast(ProcessEventDeprecated::Exception); + case ProcessEvent::DebugRunning: + return static_cast(ProcessEventDeprecated::DebugRunning); + case ProcessEvent::DebugSuspended: + return static_cast(ProcessEventDeprecated::DebugSuspended); + default: + std::abort(); + } + } + + /* Process Tracking globals. */ + HosThread g_process_track_thread; + SessionManagerBase *g_process_waitable_manager = nullptr; + + /* Process lists. */ + ProcessList g_process_list; + ProcessList g_dead_process_list; + + /* Global events. */ + IEvent *g_process_event = nullptr; + IEvent *g_hook_to_create_process_event = nullptr; + IEvent *g_hook_to_create_application_process_event = nullptr; + IEvent *g_boot_finished_event = nullptr; + + /* Process Launch synchronization globals. */ + IEvent *g_process_launch_start_event = nullptr; + HosSignal g_process_launch_finish_signal; + Result g_process_launch_result = ResultSuccess; + LaunchProcessArgs g_process_launch_args = {}; + + /* Hook globals. */ + std::atomic g_title_id_hook; + std::atomic g_application_hook; + + /* Helpers. */ + void ProcessTrackingMain(void *arg) { + /* This is the main loop of the process tracking thread. */ + + /* Create waitable manager. */ + static auto s_process_waiter = WaitableManager(1); + g_process_waitable_manager = &s_process_waiter; + + /* Service processes. */ + g_process_waitable_manager->AddWaitable(g_process_launch_start_event); + g_process_waitable_manager->Process(); + } + + inline u32 GetLoaderCreateProcessFlags(u32 launch_flags) { + u32 ldr_flags = 0; + + if (ShouldSignalOnException(launch_flags) || (GetRuntimeFirmwareVersion() >= FirmwareVersion_200 && !ShouldStartSuspended(launch_flags))) { + ldr_flags |= ldr::CreateProcessFlag_EnableDebug; + } + if (ShouldDisableAslr(launch_flags)) { + ldr_flags |= ldr::CreateProcessFlag_DisableAslr; + } + + return ldr_flags; + } + + Result StartProcess(std::shared_ptr process_info, const ldr::ProgramInfo *program_info) { + R_TRY(svcStartProcess(process_info->GetHandle(), program_info->main_thread_priority, program_info->default_cpu_id, program_info->main_thread_stack_size)); + process_info->SetState(ProcessState_Running); + return ResultSuccess; + } + + Result LaunchProcess(const LaunchProcessArgs *args) { + /* Get Program Info. */ + ldr::ProgramInfo program_info; + R_TRY(ldr::pm::GetProgramInfo(&program_info, args->location)); + const bool is_application = (program_info.flags & ldr::ProgramInfoFlag_ApplicationTypeMask) == ldr::ProgramInfoFlag_Application; + const bool allow_debug = (program_info.flags & ldr::ProgramInfoFlag_AllowDebug) || GetRuntimeFirmwareVersion() < FirmwareVersion_200; + + /* Ensure we only try to run one application. */ + if (is_application) { + return ResultPmApplicationRunning; + } + + /* Fix the title location to use the right title id. */ + const ncm::TitleLocation location = ncm::MakeTitleLocation(program_info.title_id, static_cast(args->location.storage_id)); + + /* Pin the program with loader. */ + ldr::PinId pin_id; + R_TRY(ldr::pm::PinTitle(&pin_id, location)); + + /* Ensure resources are available. */ + resource::WaitResourceAvailable(&program_info); + + /* Actually create the process. */ + Handle process_handle; + R_TRY_CLEANUP(ldr::pm::CreateProcess(&process_handle, pin_id, GetLoaderCreateProcessFlags(args->flags), resource::GetResourceLimitHandle(&program_info)), { + ldr::pm::UnpinTitle(pin_id); + }); + + /* Get the process id. */ + u64 process_id; + R_ASSERT(svcGetProcessId(&process_id, process_handle)); + + /* Make new process info. */ + auto process_info = std::make_shared(process_handle, process_id, pin_id, location); + + 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(process_id, static_cast(location.title_id), static_cast(location.storage_id), aci_fah, program_info.aci_fah_size, acid_fac, program_info.acid_fac_size)); + R_TRY(sm::manager::RegisterProcess(process_id, 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) { + process_info->SetSignalOnStart(); + } + if (ShouldSignalOnExit(args->flags)) { + process_info->SetSignalOnExit(); + } + if (ShouldSignalOnDebugEvent(args->flags) && allow_debug) { + process_info->SetSignalOnDebugEvent(); + } + + /* Process hooks/signaling. */ + if (location.title_id == g_title_id_hook) { + g_hook_to_create_process_event->Signal(); + g_title_id_hook = ncm::InvalidTitleId; + } else if (is_application && g_application_hook) { + g_hook_to_create_application_process_event->Signal(); + g_application_hook = false; + } else if (!ShouldStartSuspended(args->flags)) { + R_TRY(StartProcess(process_info, &program_info)); + } + + /* Add process to list. */ + { + ProcessListAccessor list(g_process_list); + list->Add(process_info); + g_process_waitable_manager->AddWaitable(new ProcessInfoWaiter(process_info)); + } + + *args->out_process_id = process_id; + return ResultSuccess; + } + + Result LaunchProcessEventCallback(u64 timeout) { + g_process_launch_start_event->Clear(); + g_process_launch_result = LaunchProcess(&g_process_launch_args); + g_process_launch_finish_signal.Signal(); + return ResultSuccess; + } + + void CleanupProcess(ProcessListAccessor &list, std::shared_ptr process_info) { + /* Remove the process from the list. */ + list->Remove(process_info->GetProcessId()); + + /* Close process resources. */ + process_info->Cleanup(); + + /* Handle the case where we need to keep the process alive some time longer. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500 && process_info->ShouldSignalOnExit()) { + /* Add the process to the list of dead processes. */ + { + ProcessListAccessor dead_list(g_dead_process_list); + dead_list->Add(process_info); + } + /* Signal. */ + g_process_event->Signal(); + } + } + + } + + /* Initialization. */ + Result InitializeProcessManager() { + /* Create events. */ + g_process_event = CreateWriteOnlySystemEvent(); + g_hook_to_create_process_event = CreateWriteOnlySystemEvent(); + g_hook_to_create_application_process_event = CreateWriteOnlySystemEvent(); + g_boot_finished_event = CreateWriteOnlySystemEvent(); + + /* Process launch is signaled via non-system event. */ + g_process_launch_start_event = CreateHosEvent(&LaunchProcessEventCallback); + + /* Initialize resource limits. */ + R_TRY(resource::InitializeResourceManager()); + + /* Start thread. */ + R_ASSERT(g_process_track_thread.Initialize(&ProcessTrackingMain, nullptr, 0x4000, 0x15)); + R_ASSERT(g_process_track_thread.Start()); + + return ResultSuccess; + } + + /* Process Info API. */ + Result OnProcessSignaled(std::shared_ptr process_info) { + /* Resest the process's signal. */ + svcResetSignal(process_info->GetHandle()); + + /* Update the process's state. */ + const ProcessState old_state = process_info->GetState(); + { + u64 tmp = 0; + R_ASSERT(svcGetProcessInfo(&tmp, process_info->GetHandle(), ProcessInfoType_ProcessState)); + process_info->SetState(static_cast(tmp)); + } + const ProcessState new_state = process_info->GetState(); + + /* If we're transitioning away from crashed, clear waiting attached. */ + if (old_state == ProcessState_Crashed && new_state != ProcessState_Crashed) { + process_info->ClearExceptionWaitingAttach(); + } + + switch (new_state) { + case ProcessState_Created: + case ProcessState_CreatedAttached: + case ProcessState_Exiting: + break; + case ProcessState_Running: + if (process_info->ShouldSignalOnDebugEvent()) { + process_info->ClearSuspended(); + process_info->SetSuspendedStateChanged(); + g_process_event->Signal(); + } else if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200 && process_info->ShouldSignalOnStart()) { + process_info->SetStartedStateChanged(); + process_info->ClearSignalOnStart(); + g_process_event->Signal(); + } + break; + case ProcessState_Crashed: + process_info->SetExceptionOccurred(); + g_process_event->Signal(); + break; + case ProcessState_RunningAttached: + if (process_info->ShouldSignalOnDebugEvent()) { + process_info->ClearSuspended(); + process_info->SetSuspendedStateChanged(); + g_process_event->Signal(); + } + break; + case ProcessState_Exited: + if (GetRuntimeFirmwareVersion() < FirmwareVersion_500 && process_info->ShouldSignalOnExit()) { + g_process_event->Signal(); + } else { + ProcessListAccessor list(g_process_list); + CleanupProcess(list, process_info); + } + /* Return ConnectionClosed to cause libstratosphere to stop waiting on the process. */ + return ResultKernelConnectionClosed; + case ProcessState_DebugSuspended: + if (process_info->ShouldSignalOnDebugEvent()) { + process_info->SetSuspended(); + process_info->SetSuspendedStateChanged(); + g_process_event->Signal(); + } + break; + } + + return ResultSuccess; + } + + /* Process Management. */ + Result LaunchTitle(u64 *out_process_id, const ncm::TitleLocation &loc, u32 flags) { + /* Ensure we only try to launch one title at a time. */ + static HosMutex s_lock; + std::scoped_lock lk(s_lock); + + /* Set global arguments, signal, wait. */ + g_process_launch_args = { + .out_process_id = out_process_id, + .location = loc, + .flags = flags, + }; + g_process_launch_finish_signal.Reset(); + g_process_launch_start_event->Signal(); + g_process_launch_finish_signal.Wait(); + + return g_process_launch_result; + } + + Result StartProcess(u64 process_id) { + ProcessListAccessor list(g_process_list); + + auto process_info = list->Find(process_id); + if (process_info == nullptr) { + return ResultPmProcessNotFound; + } + + if (process_info->HasStarted()) { + return ResultPmAlreadyStarted; + } + + ldr::ProgramInfo program_info; + R_TRY(ldr::pm::GetProgramInfo(&program_info, process_info->GetTitleLocation())); + return StartProcess(process_info, &program_info); + } + + Result TerminateProcess(u64 process_id) { + ProcessListAccessor list(g_process_list); + + auto process_info = list->Find(process_id); + if (process_info == nullptr) { + return ResultPmProcessNotFound; + } + + return svcTerminateProcess(process_info->GetHandle()); + } + + Result TerminateTitle(ncm::TitleId title_id) { + ProcessListAccessor list(g_process_list); + + auto process_info = list->Find(title_id); + if (process_info == nullptr) { + return ResultPmProcessNotFound; + } + + return svcTerminateProcess(process_info->GetHandle()); + } + + Result GetProcessEventHandle(Handle *out) { + return g_process_event->GetHandle(); + } + + Result GetProcessEventInfo(ProcessEventInfo *out) { + /* Check for event from current process. */ + { + ProcessListAccessor list(g_process_list); + + for (size_t i = 0; i < list->GetSize(); i++) { + auto process_info = list[i]; + if (process_info->HasStarted() && process_info->HasStartedStateChanged()) { + out->event = GetProcessEventValue(ProcessEvent::Started); + out->process_id = process_info->GetProcessId(); + return ResultSuccess; + } + if (process_info->HasSuspendedStateChanged()) { + if (process_info->IsSuspended()) { + out->event = GetProcessEventValue(ProcessEvent::DebugSuspended); + } else { + out->event = GetProcessEventValue(ProcessEvent::DebugRunning); + } + out->process_id = process_info->GetProcessId(); + return ResultSuccess; + } + if (process_info->HasExceptionOccurred()) { + process_info->ClearExceptionOccurred(); + out->event = GetProcessEventValue(ProcessEvent::Exception); + out->process_id = process_info->GetProcessId(); + return ResultSuccess; + } + if (GetRuntimeFirmwareVersion() < FirmwareVersion_500 && process_info->ShouldSignalOnExit() && process_info->HasExited()) { + out->event = GetProcessEventValue(ProcessEvent::Exited); + out->process_id = process_info->GetProcessId(); + return ResultSuccess; + } + } + } + + /* Check for event from exited process. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + ProcessListAccessor dead_list(g_dead_process_list); + + if (dead_list->GetSize() > 0) { + auto process_info = dead_list->Pop(); + out->event = GetProcessEventValue(ProcessEvent::Exited); + out->process_id = process_info->GetProcessId(); + return ResultSuccess; + } + } + + out->process_id = 0; + out->event = GetProcessEventValue(ProcessEvent::None); + return ResultSuccess; + } + + Result CleanupProcess(u64 process_id) { + ProcessListAccessor list(g_process_list); + + auto process_info = list->Find(process_id); + if (process_info == nullptr) { + return ResultPmProcessNotFound; + } + + if (!process_info->HasExited()) { + return ResultPmNotExited; + } + + CleanupProcess(list, process_info); + return ResultSuccess; + } + + Result ClearExceptionOccurred(u64 process_id) { + ProcessListAccessor list(g_process_list); + + auto process_info = list->Find(process_id); + if (process_info == nullptr) { + return ResultPmProcessNotFound; + } + + process_info->ClearExceptionOccurred(); + return ResultSuccess; + } + + /* Information Getters. */ + Result GetModuleIdList(u32 *out_count, u8 *out_buf, size_t max_out_count, u64 unused) { + /* This function was always stubbed... */ + *out_count = 0; + return ResultSuccess; + } + + Result GetExceptionProcessIdList(u32 *out_count, u64 *out_process_ids, size_t max_out_count) { + ProcessListAccessor list(g_process_list); + + size_t count = 0; + for (size_t i = 0; i < list->GetSize() && count < max_out_count; i++) { + auto process_info = list[i]; + if (process_info->HasExceptionWaitingAttach()) { + out_process_ids[count++] = process_info->GetProcessId(); + } + } + + *out_count = static_cast(count); + return ResultSuccess; + } + + Result GetProcessId(u64 *out, ncm::TitleId title_id) { + ProcessListAccessor list(g_process_list); + + auto process_info = list->Find(title_id); + if (process_info == nullptr) { + return ResultPmProcessNotFound; + } + + *out = process_info->GetProcessId(); + return ResultSuccess; + } + + Result GetTitleId(ncm::TitleId *out, u64 process_id) { + ProcessListAccessor list(g_process_list); + + auto process_info = list->Find(process_id); + if (process_info == nullptr) { + return ResultPmProcessNotFound; + } + + *out = process_info->GetTitleLocation().title_id; + return ResultSuccess; + } + + Result GetApplicationProcessId(u64 *out_process_id) { + ProcessListAccessor list(g_process_list); + + for (size_t i = 0; i < list->GetSize(); i++) { + auto process_info = list[i]; + if (process_info->IsApplication()) { + *out_process_id = process_info->GetProcessId(); + return ResultSuccess; + } + } + + return ResultPmProcessNotFound; + } + + Result AtmosphereGetProcessInfo(Handle *out_process_handle, ncm::TitleLocation *out_loc, u64 process_id) { + ProcessListAccessor list(g_process_list); + + auto process_info = list->Find(process_id); + if (process_info == nullptr) { + return ResultPmProcessNotFound; + } + + *out_process_handle = process_info->GetHandle(); + *out_loc = process_info->GetTitleLocation(); + return ResultSuccess; + } + + /* Hook API. */ + Result HookToCreateProcess(Handle *out_hook, ncm::TitleId title_id) { + *out_hook = INVALID_HANDLE; + + ncm::TitleId old_value = ncm::InvalidTitleId; + if (!g_title_id_hook.compare_exchange_strong(old_value, title_id)) { + return ResultPmDebugHookInUse; + } + + *out_hook = g_hook_to_create_process_event->GetHandle(); + return ResultSuccess; + } + + Result HookToCreateApplicationProcess(Handle *out_hook) { + *out_hook = INVALID_HANDLE; + + bool old_value = false; + if (!g_application_hook.compare_exchange_strong(old_value, true)) { + return ResultPmDebugHookInUse; + } + + *out_hook = g_hook_to_create_application_process_event->GetHandle(); + return ResultSuccess; + } + + Result ClearHook(u32 which) { + if (which & HookType_TitleId) { + g_title_id_hook = ncm::InvalidTitleId; + } + if (which & HookType_Application) { + g_application_hook = false; + } + return ResultSuccess; + } + + /* Boot API. */ + Result NotifyBootFinished() { + static bool g_has_boot_finished = false; + if (!g_has_boot_finished) { + boot2::LaunchBootPrograms(); + g_has_boot_finished = true; + g_boot_finished_event->Signal(); + } + return ResultSuccess; + } + + Result GetBootFinishedEventHandle(Handle *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. */ + if (!spl::IsRecoveryBoot()) { + std::abort(); + } + *out = g_boot_finished_event->GetHandle(); + return ResultSuccess; + } + + /* Resource Limit API. */ + Result BoostSystemMemoryResourceLimit(u64 boost_size) { + return resource::BoostSystemMemoryResourceLimit(boost_size); + } + + Result BoostSystemThreadResourceLimit() { + return resource::BoostSystemThreadResourceLimit(); + } + + Result AtmosphereGetCurrentLimitInfo(u64 *out_cur_val, u64 *out_lim_val, u32 group, u32 resource) { + return resource::GetResourceLimitValues(out_cur_val, out_lim_val, static_cast(group), static_cast(resource)); + } + +} diff --git a/stratosphere/pm/source/impl/pm_process_manager.hpp b/stratosphere/pm/source/impl/pm_process_manager.hpp new file mode 100644 index 000000000..0d810fb35 --- /dev/null +++ b/stratosphere/pm/source/impl/pm_process_manager.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include + +namespace sts::pm::impl { + + /* Initialization. */ + Result InitializeProcessManager(); + + /* Process Management. */ + Result LaunchTitle(u64 *out_process_id, const ncm::TitleLocation &loc, u32 flags); + Result StartProcess(u64 process_id); + Result TerminateProcess(u64 process_id); + Result TerminateTitle(ncm::TitleId title_id); + Result GetProcessEventHandle(Handle *out); + Result GetProcessEventInfo(ProcessEventInfo *out); + Result CleanupProcess(u64 process_id); + Result ClearExceptionOccurred(u64 process_id); + + /* Information Getters. */ + Result GetModuleIdList(u32 *out_count, u8 *out_buf, size_t max_out_count, u64 unused); + Result GetExceptionProcessIdList(u32 *out_count, u64 *out_process_ids, size_t max_out_count); + Result GetProcessId(u64 *out, ncm::TitleId title_id); + Result GetTitleId(ncm::TitleId *out, u64 process_id); + Result GetApplicationProcessId(u64 *out_process_id); + Result AtmosphereGetProcessInfo(Handle *out_process_handle, ncm::TitleLocation *out_loc, u64 process_id); + + /* Hook API. */ + Result HookToCreateProcess(Handle *out_hook, ncm::TitleId title_id); + Result HookToCreateApplicationProcess(Handle *out_hook); + Result ClearHook(u32 which); + + /* Boot API. */ + Result NotifyBootFinished(); + Result GetBootFinishedEventHandle(Handle *out); + + /* Resource Limit API. */ + Result BoostSystemMemoryResourceLimit(u64 boost_size); + Result BoostSystemThreadResourceLimit(); + Result AtmosphereGetCurrentLimitInfo(u64 *out_cur_val, u64 *out_lim_val, u32 group, u32 resource); + +} diff --git a/stratosphere/pm/source/impl/pm_resource_manager.cpp b/stratosphere/pm/source/impl/pm_resource_manager.cpp new file mode 100644 index 000000000..321aa66d0 --- /dev/null +++ b/stratosphere/pm/source/impl/pm_resource_manager.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "pm_resource_manager.hpp" + +namespace sts::pm::resource { + + namespace { + + constexpr LimitableResource LimitableResources[] = { + LimitableResource_Memory, + LimitableResource_Threads, + LimitableResource_Events, + LimitableResource_TransferMemories, + LimitableResource_Sessions, + }; + constexpr size_t LimitableResource_Count = sizeof(LimitableResources) / sizeof(LimitableResources[0]); + + constexpr size_t Megabyte = 0x100000; + + /* Definitions for limit differences over time. */ + constexpr size_t ExtraSystemThreadCount400 = 100; + constexpr size_t ExtraSystemMemorySize400 = 10 * Megabyte; + constexpr size_t ExtraSystemMemorySize500 = 12 * Megabyte; + constexpr size_t ExtraSystemEventCount600 = 100; + constexpr size_t ExtraSystemSessionCount600 = 100; + constexpr size_t ReservedMemorySize600 = 5 * Megabyte; + + /* Atmosphere always allocates 24 extra megabytes for system usage. */ + constexpr size_t ExtraSystemMemorySizeAtmosphere = 24 * Megabyte; + + /* Globals. */ + HosMutex g_resource_limit_lock; + Handle g_resource_limit_handles[ResourceLimitGroup_Count]; + spl::MemoryArrangement g_memory_arrangement = spl::MemoryArrangement_Standard; + u64 g_system_memory_boost_size = 0; + + u64 g_resource_limits[ResourceLimitGroup_Count][LimitableResource_Count] = { + [ResourceLimitGroup_System] = { + [LimitableResource_Memory] = 0, /* Initialized by more complicated logic later. */ + [LimitableResource_Threads] = 508, + [LimitableResource_Events] = 600, + [LimitableResource_TransferMemories] = 128, + [LimitableResource_Sessions] = 794, + }, + [ResourceLimitGroup_Application] = { + [LimitableResource_Memory] = 0, /* Initialized by more complicated logic later. */ + [LimitableResource_Threads] = 96, + [LimitableResource_Events] = 0, + [LimitableResource_TransferMemories] = 32, + [LimitableResource_Sessions] = 1, + }, + [ResourceLimitGroup_Applet] = { + [LimitableResource_Memory] = 0, /* Initialized by more complicated logic later. */ + [LimitableResource_Threads] = 96, + [LimitableResource_Events] = 0, + [LimitableResource_TransferMemories] = 32, + [LimitableResource_Sessions] = 5, + }, + }; + + u64 g_memory_resource_limits[spl::MemoryArrangement_Count][ResourceLimitGroup_Count] = { + [spl::MemoryArrangement_Standard] = { + [ResourceLimitGroup_System] = 269 * Megabyte, + [ResourceLimitGroup_Application] = 3285 * Megabyte, + [ResourceLimitGroup_Applet] = 535 * Megabyte, + }, + [spl::MemoryArrangement_StandardForAppletDev] = { + [ResourceLimitGroup_System] = 481 * Megabyte, + [ResourceLimitGroup_Application] = 2048 * Megabyte, + [ResourceLimitGroup_Applet] = 1560 * Megabyte, + }, + [spl::MemoryArrangement_StandardForSystemDev] = { + [ResourceLimitGroup_System] = 328 * Megabyte, + [ResourceLimitGroup_Application] = 3285 * Megabyte, + [ResourceLimitGroup_Applet] = 476 * Megabyte, + }, + [spl::MemoryArrangement_Expanded] = { + [ResourceLimitGroup_System] = 653 * Megabyte, + [ResourceLimitGroup_Application] = 4916 * Megabyte, + [ResourceLimitGroup_Applet] = 568 * Megabyte, + }, + [spl::MemoryArrangement_ExpandedForAppletDev] = { + [ResourceLimitGroup_System] = 653 * Megabyte, + [ResourceLimitGroup_Application] = 3285 * Megabyte, + [ResourceLimitGroup_Applet] = 2199 * Megabyte, + }, + }; + + /* Helpers. */ + Result SetMemoryResourceLimitLimitValue(ResourceLimitGroup group, u64 new_memory_limit) { + const u64 old_memory_limit = g_resource_limits[group][LimitableResource_Memory]; + g_resource_limits[group][LimitableResource_Memory] = new_memory_limit; + R_TRY_CLEANUP(svcSetResourceLimitLimitValue(GetResourceLimitHandle(group), LimitableResource_Memory, g_resource_limits[group][LimitableResource_Memory]), { + /* If we fail, restore the old memory limit. */ + g_resource_limits[group][LimitableResource_Memory] = old_memory_limit; + }); + return ResultSuccess; + } + + 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 < LimitableResource_Count; i++) { + const auto resource = LimitableResources[i]; + if (resource == LimitableResource_Memory) { + continue; + } + R_TRY(svcSetResourceLimitLimitValue(GetResourceLimitHandle(group), LimitableResources[resource], g_resource_limits[group][resource])); + } + return ResultSuccess; + } + + 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 Handle reslimit_hnd = GetResourceLimitHandle(group); + for (size_t i = 0; i < LimitableResource_Count; i++) { + const auto resource = LimitableResources[i]; + + u64 value = 0; + while (true) { + R_ASSERT(svcGetResourceLimitCurrentValue(&value, reslimit_hnd, resource)); + if (value == 0) { + break; + } + svcSleepThread(1'000'000ul); + } + } + } + + void WaitApplicationMemoryAvailable() { + u64 value = 0; + while (true) { + R_ASSERT(svcGetSystemInfo(&value, SystemInfoType_UsedPhysicalMemorySize, INVALID_HANDLE, PhysicalMemoryInfo_Application)); + if (value == 0) { + break; + } + svcSleepThread(1'000'000ul); + } + } + + } + + /* Resource API. */ + Result InitializeResourceManager() { + /* Create resource limit handles. */ + for (size_t i = 0; i < ResourceLimitGroup_Count; i++) { + if (i == ResourceLimitGroup_System) { + u64 value = 0; + R_ASSERT(svcGetInfo(&value, InfoType_ResourceLimit, INVALID_HANDLE, 0)); + g_resource_limit_handles[i] = static_cast(value); + } else { + R_ASSERT(svcCreateResourceLimit(&g_resource_limit_handles[i])); + } + } + + /* Adjust resource limits based on firmware version. */ + const auto firmware_version = GetRuntimeFirmwareVersion(); + if (firmware_version >= FirmwareVersion_400) { + /* 4.0.0 increased the system thread limit. */ + g_resource_limits[ResourceLimitGroup_System][LimitableResource_Threads] += ExtraSystemThreadCount400; + /* 4.0.0 also 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; + } + if (firmware_version >= FirmwareVersion_500) { + /* 5.0.0 took more memory away from applet and gave it to system, for the Standard and StandardForSystemDev profiles. */ + g_memory_resource_limits[spl::MemoryArrangement_Standard][ResourceLimitGroup_System] += ExtraSystemMemorySize500; + g_memory_resource_limits[spl::MemoryArrangement_Standard][ResourceLimitGroup_Applet] -= ExtraSystemMemorySize500; + g_memory_resource_limits[spl::MemoryArrangement_StandardForSystemDev][ResourceLimitGroup_System] += ExtraSystemMemorySize500; + g_memory_resource_limits[spl::MemoryArrangement_StandardForSystemDev][ResourceLimitGroup_Applet] -= ExtraSystemMemorySize500; + } + if (firmware_version >= FirmwareVersion_600) { + /* 6.0.0 increased the system event and session limits. */ + g_resource_limits[ResourceLimitGroup_System][LimitableResource_Events] += ExtraSystemEventCount600; + g_resource_limits[ResourceLimitGroup_System][LimitableResource_Sessions] += ExtraSystemSessionCount600; + } + + /* 7.0.0+: Nintendo restricts the number of system threads here, from 0x260 -> 0x60. */ + /* We will not do this. */ + + /* Choose and initialize memory arrangement. */ + if (firmware_version >= FirmwareVersion_600) { + /* 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. */ + u64 total_memory = 0; + R_ASSERT(svcGetResourceLimitLimitValue(&total_memory, GetResourceLimitHandle(ResourceLimitGroup_System), LimitableResource_Memory)); + + /* Get and save application + applet memory. */ + R_ASSERT(svcGetSystemInfo(&g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Application], SystemInfoType_TotalPhysicalMemorySize, INVALID_HANDLE, PhysicalMemoryInfo_Application)); + R_ASSERT(svcGetSystemInfo(&g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Applet], SystemInfoType_TotalPhysicalMemorySize, INVALID_HANDLE, PhysicalMemoryInfo_Applet)); + + const u64 application_size = g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Application]; + const u64 applet_size = g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Applet]; + const u64 reserved_non_system_size = (application_size + applet_size + ReservedMemorySize600); + + /* Ensure there's enough memory for the system region. */ + if (reserved_non_system_size >= total_memory) { + std::abort(); + } + + g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_System] = total_memory - reserved_non_system_size; + } else { + 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. */ + for (size_t i = 0; i < spl::MemoryArrangement_Count; i++) { + g_memory_resource_limits[i][ResourceLimitGroup_System] += ExtraSystemMemorySizeAtmosphere; + if (firmware_version >= FirmwareVersion_300) { + g_memory_resource_limits[i][ResourceLimitGroup_Applet] -= ExtraSystemMemorySizeAtmosphere; + } else { + g_memory_resource_limits[i][ResourceLimitGroup_Application] -= ExtraSystemMemorySizeAtmosphere; + } + } + + /* Actually set resource limits. */ + { + std::scoped_lock lk(g_resource_limit_lock); + + for (size_t group = 0; group < ResourceLimitGroup_Count; group++) { + R_ASSERT(SetResourceLimitLimitValues(static_cast(group), g_memory_resource_limits[g_memory_arrangement][group])); + } + } + + return ResultSuccess; + } + + Result BoostSystemMemoryResourceLimit(u64 boost_size) { + /* Don't allow all application memory to be taken away. */ + if (boost_size > g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application]) { + return ResultPmInvalidSize; + } + + const u64 new_app_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application] - boost_size; + { + std::scoped_lock lk(g_resource_limit_lock); + + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + /* Starting in 5.0.0, PM does not allow for only one of the sets to fail. */ + if (boost_size < g_system_memory_boost_size) { + R_TRY(svcSetUnsafeLimit(boost_size)); + R_ASSERT(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size)); + } else { + R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size)); + R_ASSERT(svcSetUnsafeLimit(boost_size)); + } + } else { + const u64 new_sys_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_System] + boost_size; + if (boost_size < g_system_memory_boost_size) { + R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System, new_sys_size)); + R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size)); + } else { + R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size)); + R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System, new_sys_size)); + } + } + + g_system_memory_boost_size = boost_size; + } + + return ResultSuccess; + } + + Result BoostSystemThreadResourceLimit() { + /* Starting in 7.0.0, Nintendo reduces the number of system threads from 0x260 to 0x60, */ + /* Until this command is called to double that amount to 0xC0. */ + /* We will simply not reduce the number of system threads available for no reason. */ + return ResultSuccess; + } + + Handle GetResourceLimitHandle(ResourceLimitGroup group) { + return g_resource_limit_handles[group]; + } + + Handle GetResourceLimitHandle(const ldr::ProgramInfo *info) { + return GetResourceLimitHandle(GetResourceLimitGroup(info)); + } + + void WaitResourceAvailable(const ldr::ProgramInfo *info) { + if (GetResourceLimitGroup(info) == ResourceLimitGroup_Application) { + WaitResourceAvailable(ResourceLimitGroup_Application); + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + WaitApplicationMemoryAvailable(); + } + } + } + + Result GetResourceLimitValues(u64 *out_cur, u64 *out_lim, ResourceLimitGroup group, LimitableResource resource) { + /* Do not allow out of bounds access. */ + if (group >= ResourceLimitGroup_Count || resource >= LimitableResource_Count) { + std::abort(); + } + + const Handle reslimit_hnd = GetResourceLimitHandle(group); + R_TRY(svcGetResourceLimitCurrentValue(out_cur, reslimit_hnd, resource)); + R_TRY(svcGetResourceLimitLimitValue(out_lim, reslimit_hnd, resource)); + + return ResultSuccess; + } + +} diff --git a/stratosphere/pm/source/pm_resource_limits.hpp b/stratosphere/pm/source/impl/pm_resource_manager.hpp similarity index 55% rename from stratosphere/pm/source/pm_resource_limits.hpp rename to stratosphere/pm/source/impl/pm_resource_manager.hpp index 58bb9ebc1..b43f95154 100644 --- a/stratosphere/pm/source/pm_resource_limits.hpp +++ b/stratosphere/pm/source/impl/pm_resource_manager.hpp @@ -17,17 +17,19 @@ #pragma once #include #include +#include +#include -class ResourceLimitUtils { - public: - enum ResourceLimitCategory { - ResourceLimitCategory_System = 0, - ResourceLimitCategory_Application = 1, - ResourceLimitCategory_Applet = 2 - }; - static void InitializeLimits(); - static void EnsureApplicationResourcesAvailable(); - static Handle GetResourceLimitHandle(u16 application_type); - static Handle GetResourceLimitHandleByCategory(ResourceLimitCategory category); - static Result BoostSystemMemoryResourceLimit(u64 boost_size); -}; \ No newline at end of file +namespace sts::pm::resource { + + /* Resource API. */ + Result InitializeResourceManager(); + Result BoostSystemMemoryResourceLimit(u64 boost_size); + Result BoostSystemThreadResourceLimit(); + Handle GetResourceLimitHandle(ResourceLimitGroup group); + Handle GetResourceLimitHandle(const ldr::ProgramInfo *info); + void WaitResourceAvailable(const ldr::ProgramInfo *info); + + Result GetResourceLimitValues(u64 *out_cur, u64 *out_lim, ResourceLimitGroup group, LimitableResource resource); + +} diff --git a/stratosphere/pm/source/pm_boot2.cpp b/stratosphere/pm/source/pm_boot2.cpp deleted file mode 100644 index 96ca703b5..000000000 --- a/stratosphere/pm/source/pm_boot2.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "pm_boot2.hpp" -#include "pm_registration.hpp" -#include "pm_boot_mode.hpp" - - -static bool IsHexadecimal(const char *str) { - while (*str) { - if (isxdigit((unsigned char)*str)) { - str++; - } else { - return false; - } - } - return true; -} - -static void LaunchTitle(u64 title_id, FsStorageId storage_id, u32 launch_flags, u64 *pid) { - u64 local_pid = 0; - - /* Don't launch a title twice during boot2. */ - if (Registration::HasLaunchedTitle(title_id)) { - return; - } - - switch (Registration::LaunchProcessByTidSid(Registration::TidSid{title_id, storage_id}, launch_flags, &local_pid)) { - case ResultKernelResourceExhausted: - /* Out of resource! */ - std::abort(); - case ResultKernelOutOfMemory: - /* Out of memory! */ - std::abort(); - case ResultKernelLimitReached: - /* Limit Reached! */ - std::abort(); - default: - /* We don't care about other issues. */ - break; - } - - if (pid) { - *pid = local_pid; - } -} - -static bool GetGpioPadLow(GpioPadName pad) { - GpioPadSession button; - if (R_FAILED(gpioOpenSession(&button, pad))) { - return false; - } - - /* Ensure we close even on early return. */ - ON_SCOPE_EXIT { gpioPadClose(&button); }; - - /* Set direction input. */ - gpioPadSetDirection(&button, GpioDirection_Input); - - GpioValue val; - return R_SUCCEEDED(gpioPadGetValue(&button, &val)) && val == GpioValue_Low; -} - -static bool IsMaintenanceMode() { - /* Contact set:sys, retrieve boot!force_maintenance. */ - DoWithSmSession([&]() { - R_ASSERT(setsysInitialize()); - }); - { - ON_SCOPE_EXIT { setsysExit(); }; - - u8 force_maintenance = 1; - setsysGetSettingsItemValue("boot", "force_maintenance", &force_maintenance, sizeof(force_maintenance)); - if (force_maintenance != 0) { - return true; - } - } - - /* Contact GPIO, read plus/minus buttons. */ - DoWithSmSession([&]() { - R_ASSERT(gpioInitialize()); - }); - { - ON_SCOPE_EXIT { gpioExit(); }; - - return GetGpioPadLow(GpioPadName_ButtonVolUp) && GetGpioPadLow(GpioPadName_ButtonVolDown); - } - - return false; -} - -static const std::tuple g_additional_launch_programs[] = { - {TitleId_Am, true}, /* am */ - {TitleId_NvServices, true}, /* nvservices */ - {TitleId_NvnFlinger, true}, /* nvnflinger */ - {TitleId_Vi, true}, /* vi */ - {TitleId_Ns, true}, /* ns */ - {TitleId_LogManager, true}, /* lm */ - {TitleId_Ppc, true}, /* ppc */ - {TitleId_Ptm, true}, /* ptm */ - {TitleId_Hid, true}, /* hid */ - {TitleId_Audio, true}, /* audio */ - {TitleId_Lbl, true}, /* lbl */ - {TitleId_Wlan, true}, /* wlan */ - {TitleId_Bluetooth, true}, /* bluetooth */ - {TitleId_BsdSockets, true}, /* bsdsockets */ - {TitleId_Nifm, true}, /* nifm */ - {TitleId_Ldn, true}, /* ldn */ - {TitleId_Account, true}, /* account */ - {TitleId_Friends, false}, /* friends */ - {TitleId_Nfc, true}, /* nfc */ - {TitleId_JpegDec, true}, /* jpegdec */ - {TitleId_CapSrv, true}, /* capsrv */ - {TitleId_Ssl, true}, /* ssl */ - {TitleId_Nim, true}, /* nim */ - {TitleId_Bcat, false}, /* bcat */ - {TitleId_Erpt, true}, /* erpt */ - {TitleId_Es, true}, /* es */ - {TitleId_Pctl, true}, /* pctl */ - {TitleId_Btm, true}, /* btm */ - {TitleId_Eupld, false}, /* eupld */ - {TitleId_Glue, true}, /* glue */ - /* {TitleId_Eclct, true}, */ /* eclct */ /* Skip launching error collection in Atmosphere to lessen telemetry. */ - {TitleId_Npns, false}, /* npns */ - {TitleId_Fatal, true}, /* fatal */ - {TitleId_Ro, true}, /* ro */ - {TitleId_Profiler, true}, /* profiler */ - {TitleId_Sdb, true}, /* sdb */ - {TitleId_Migration, true}, /* migration */ - {TitleId_Grc, true}, /* grc */ - {TitleId_Olsc, true}, /* olsc */ -}; - -static void MountSdCard() { - DoWithSmSession([&]() { - Handle tmp_hnd = 0; - static const char * const required_active_services[] = {"pcv", "gpio", "pinmux", "psc:c"}; - for (unsigned int i = 0; i < sizeof(required_active_services) / sizeof(required_active_services[0]); i++) { - R_ASSERT(smGetServiceOriginal(&tmp_hnd, smEncodeName(required_active_services[i]))); - svcCloseHandle(tmp_hnd); - } - }); - fsdevMountSdmc(); -} - -static void WaitForMitm(const char *service) { - const auto name = sts::sm::ServiceName::Encode(service); - - while (true) { - bool mitm_installed = false; - R_ASSERT(sts::sm::manager::HasMitm(&mitm_installed, name)); - if (mitm_installed) { - break; - } - svcSleepThread(1000000ull); - } -} - -void EmbeddedBoot2::Main() { - /* Wait until fs.mitm has installed itself. We want this to happen as early as possible. */ - WaitForMitm("fsp-srv"); - - /* psc, bus, pcv is the minimal set of required titles to get SD card. */ - /* bus depends on pcie, and pcv depends on settings. */ - /* Launch psc. */ - LaunchTitle(TitleId_Psc, FsStorageId_NandSystem, 0, NULL); - /* Launch pcie. */ - LaunchTitle(TitleId_Pcie, FsStorageId_NandSystem, 0, NULL); - /* Launch bus. */ - LaunchTitle(TitleId_Bus, FsStorageId_NandSystem, 0, NULL); - /* Launch settings. */ - LaunchTitle(TitleId_Settings, FsStorageId_NandSystem, 0, NULL); - /* Launch pcv. */ - LaunchTitle(TitleId_Pcv, FsStorageId_NandSystem, 0, NULL); - - /* At this point, the SD card can be mounted. */ - MountSdCard(); - - /* Find out whether we are maintenance mode. */ - bool maintenance = IsMaintenanceMode(); - if (maintenance) { - BootModeService::SetMaintenanceBootForEmbeddedBoot2(); - } - - /* Wait for other atmosphere mitm modules to initialize. */ - WaitForMitm("set:sys"); - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) { - WaitForMitm("bpc"); - } else { - WaitForMitm("bpc:c"); - } - - /* Launch usb. */ - LaunchTitle(TitleId_Usb, FsStorageId_NandSystem, 0, NULL); - - /* Launch tma. */ - LaunchTitle(TitleId_Tma, FsStorageId_NandSystem, 0, NULL); - - /* Launch Atmosphere dmnt, using FsStorageId_None to force SD card boot. */ - LaunchTitle(TitleId_Dmnt, FsStorageId_None, 0, NULL); - - /* Launch default programs. */ - for (auto &launch_program : g_additional_launch_programs) { - if (!maintenance || std::get(launch_program)) { - LaunchTitle(std::get(launch_program), FsStorageId_NandSystem, 0, NULL); - } - - /* In 7.0.0, Npns was added to the list of titles to launch during maintenance. */ - if (maintenance && std::get(launch_program) == TitleId_Npns && GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { - LaunchTitle(TitleId_Npns, FsStorageId_NandSystem, 0, NULL); - } - } - - /* Allow for user-customizable programs. */ - DIR *titles_dir = opendir("sdmc:/atmosphere/titles"); - struct dirent *ent; - if (titles_dir != NULL) { - while ((ent = readdir(titles_dir)) != NULL) { - if (strlen(ent->d_name) == 0x10 && IsHexadecimal(ent->d_name)) { - u64 title_id = (u64)strtoul(ent->d_name, NULL, 16); - if (Registration::HasLaunchedTitle(title_id)) { - continue; - } - char title_path[FS_MAX_PATH] = {0}; - strcpy(title_path, "sdmc:/atmosphere/titles/"); - strcat(title_path, ent->d_name); - strcat(title_path, "/flags/boot2.flag"); - FILE *f_flag = fopen(title_path, "rb"); - if (f_flag != NULL) { - fclose(f_flag); - LaunchTitle(title_id, FsStorageId_None, 0, NULL); - } else { - /* TODO: Deprecate this in the future. */ - memset(title_path, 0, FS_MAX_PATH); - strcpy(title_path, "sdmc:/atmosphere/titles/"); - strcat(title_path, ent->d_name); - strcat(title_path, "/boot2.flag"); - f_flag = fopen(title_path, "rb"); - if (f_flag != NULL) { - fclose(f_flag); - LaunchTitle(title_id, FsStorageId_None, 0, NULL); - } - } - } - } - closedir(titles_dir); - } - - /* We no longer need the SD card. */ - fsdevUnmountAll(); -} diff --git a/stratosphere/pm/source/pm_boot2.hpp b/stratosphere/pm/source/pm_boot2.hpp deleted file mode 100644 index 30761a85e..000000000 --- a/stratosphere/pm/source/pm_boot2.hpp +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -class EmbeddedBoot2 { - public: - static void Main(); -}; \ No newline at end of file diff --git a/stratosphere/pm/source/pm_boot_mode.cpp b/stratosphere/pm/source/pm_boot_mode_service.cpp similarity index 50% rename from stratosphere/pm/source/pm_boot_mode.cpp rename to stratosphere/pm/source/pm_boot_mode_service.cpp index bb0a2e541..2af1af1e2 100644 --- a/stratosphere/pm/source/pm_boot_mode.cpp +++ b/stratosphere/pm/source/pm_boot_mode_service.cpp @@ -14,20 +14,33 @@ * along with this program. If not, see . */ -#include -#include -#include "pm_boot_mode.hpp" +#include "pm_boot_mode_service.hpp" -static bool g_is_maintenance_boot = false; +namespace sts::pm::bm { + + namespace { + + /* Global bootmode. */ + BootMode g_boot_mode = BootMode::Normal; + + } + + /* Override of weakly linked boot_mode_api functions. */ + BootMode GetBootMode() { + return g_boot_mode; + } + + void SetMaintenanceBoot() { + g_boot_mode = BootMode::Maintenance; + } + + /* Service command implementations. */ + void BootModeService::GetBootMode(Out out) { + out.SetValue(static_cast(pm::bm::GetBootMode())); + } + + void BootModeService::SetMaintenanceBoot() { + pm::bm::SetMaintenanceBoot(); + } -void BootModeService::GetBootMode(Out out) { - out.SetValue(g_is_maintenance_boot); -} - -void BootModeService::SetMaintenanceBoot() { - g_is_maintenance_boot = true; -} - -void BootModeService::SetMaintenanceBootForEmbeddedBoot2() { - g_is_maintenance_boot = true; } diff --git a/stratosphere/pm/source/pm_boot_mode.hpp b/stratosphere/pm/source/pm_boot_mode_service.hpp similarity index 52% rename from stratosphere/pm/source/pm_boot_mode.hpp rename to stratosphere/pm/source/pm_boot_mode_service.hpp index 8a3fec6a4..b70cc2713 100644 --- a/stratosphere/pm/source/pm_boot_mode.hpp +++ b/stratosphere/pm/source/pm_boot_mode_service.hpp @@ -17,22 +17,25 @@ #pragma once #include #include +#include -class BootModeService final : public IServiceObject { - private: - enum class CommandId { - GetBootMode = 0, - SetMaintenanceBoot = 1, - }; - private: - /* Actual commands. */ - void GetBootMode(Out out); - void SetMaintenanceBoot(); - public: - static void SetMaintenanceBootForEmbeddedBoot2(); - public: - DEFINE_SERVICE_DISPATCH_TABLE { - MAKE_SERVICE_COMMAND_META(BootModeService, GetBootMode), - MAKE_SERVICE_COMMAND_META(BootModeService, SetMaintenanceBoot), - }; -}; +namespace sts::pm::bm { + + class BootModeService final : public IServiceObject { + private: + enum class CommandId { + GetBootMode = 0, + SetMaintenanceBoot = 1, + }; + private: + /* Actual command implementations. */ + void GetBootMode(Out out); + void SetMaintenanceBoot(); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(BootModeService, GetBootMode), + MAKE_SERVICE_COMMAND_META(BootModeService, SetMaintenanceBoot), + }; + }; + +} diff --git a/stratosphere/pm/source/pm_debug_monitor.cpp b/stratosphere/pm/source/pm_debug_monitor.cpp deleted file mode 100644 index b546f3224..000000000 --- a/stratosphere/pm/source/pm_debug_monitor.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include "pm_registration.hpp" -#include "pm_resource_limits.hpp" -#include "pm_debug_monitor.hpp" - - -Result DebugMonitorService::GetUnknownStub(Out count, OutBuffer out_buf, u64 in_unk) { - /* This command seems stubbed. */ - if (out_buf.num_elements >> 31) { - return ResultPmInvalidSize; - } - count.SetValue(0); - return ResultSuccess; -} - -Result DebugMonitorService::GetDebugProcessIds(Out count, OutBuffer out_pids) { - if (out_pids.num_elements >> 31) { - return ResultPmInvalidSize; - } - return Registration::GetDebugProcessIds(out_pids.buffer, out_pids.num_elements, count.GetPointer()); -} - -Result DebugMonitorService::LaunchDebugProcess(u64 pid) { - return Registration::LaunchDebugProcess(pid); -} - -Result DebugMonitorService::GetTitleProcessId(Out pid, u64 tid) { - std::scoped_lock lk(Registration::GetProcessList()); - - std::shared_ptr proc = Registration::GetProcessByTitleId(tid); - if (proc != nullptr) { - pid.SetValue(proc->pid); - return ResultSuccess; - } - return ResultPmProcessNotFound; -} - -Result DebugMonitorService::EnableDebugForTitleId(Out event, u64 tid) { - return Registration::EnableDebugForTitleId(tid, event.GetHandlePointer()); -} - -Result DebugMonitorService::GetApplicationProcessId(Out pid) { - std::scoped_lock lk(Registration::GetProcessList()); - - std::shared_ptr app_proc; - if (Registration::HasApplicationProcess(&app_proc)) { - pid.SetValue(app_proc->pid); - return ResultSuccess; - } - return ResultPmProcessNotFound; -} - -Result DebugMonitorService::EnableDebugForApplication(Out event) { - return Registration::EnableDebugForApplication(event.GetHandlePointer()); -} - - -Result DebugMonitorService::DisableDebug(u32 which) { - return Registration::DisableDebug(which); -} - -Result DebugMonitorService::AtmosphereGetProcessInfo(Out proc_hand, Out tid_sid, u64 pid) { - auto proc = Registration::GetProcess(pid); - if (proc != nullptr) { - proc_hand.SetValue(proc->handle); - tid_sid.SetValue(proc->tid_sid); - return ResultSuccess; - } - return ResultPmProcessNotFound; -} - -Result DebugMonitorService::AtmosphereGetCurrentLimitInfo(Out cur_val, Out lim_val, u32 category, u32 resource) { - if(category > ResourceLimitUtils::ResourceLimitCategory::ResourceLimitCategory_Applet) { - return ResultKernelInvalidEnumValue; - } - - Handle limit_h = ResourceLimitUtils::GetResourceLimitHandleByCategory((ResourceLimitUtils::ResourceLimitCategory) category); - - R_TRY(svcGetResourceLimitCurrentValue(cur_val.GetPointer(), limit_h, (LimitableResource) resource)); - R_TRY(svcGetResourceLimitLimitValue(lim_val.GetPointer(), limit_h, (LimitableResource) resource)); - - return ResultSuccess; -} diff --git a/stratosphere/pm/source/pm_debug_monitor.hpp b/stratosphere/pm/source/pm_debug_monitor.hpp deleted file mode 100644 index 3398ba832..000000000 --- a/stratosphere/pm/source/pm_debug_monitor.hpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include -#include - -#include "pm_registration.hpp" - -/* Represents modern DebugMonitorService (5.0.0+) */ -class DebugMonitorService : public IServiceObject { - private: - enum class CommandId { - GetDebugProcessIds = 0, - LaunchDebugProcess = 1, - GetTitleProcessId = 2, - EnableDebugForTitleId = 3, - GetApplicationProcessId = 4, - EnableDebugForApplication = 5, - - DisableDebug = 6, - - AtmosphereGetProcessInfo = 65000, - AtmosphereGetCurrentLimitInfo = 65001, - }; - protected: - /* Actual commands. */ - Result GetUnknownStub(Out count, OutBuffer out_buf, u64 in_unk); - Result GetDebugProcessIds(Out count, OutBuffer out_pids); - Result LaunchDebugProcess(u64 pid); - Result GetTitleProcessId(Out pid, u64 tid); - Result EnableDebugForTitleId(Out event, u64 tid); - Result GetApplicationProcessId(Out pid); - Result EnableDebugForApplication(Out event); - Result DisableDebug(u32 which); - - /* Atmosphere commands. */ - Result AtmosphereGetProcessInfo(Out proc_hand, Out tid_sid, u64 pid); - Result AtmosphereGetCurrentLimitInfo(Out cur_val, Out lim_val, u32 category, u32 resource); - public: - DEFINE_SERVICE_DISPATCH_TABLE { - /* 5.0.0-* */ - MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetDebugProcessIds), - MAKE_SERVICE_COMMAND_META(DebugMonitorService, LaunchDebugProcess), - MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetTitleProcessId), - MAKE_SERVICE_COMMAND_META(DebugMonitorService, EnableDebugForTitleId), - MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetApplicationProcessId), - MAKE_SERVICE_COMMAND_META(DebugMonitorService, EnableDebugForApplication), - - /* 6.0.0-* */ - MAKE_SERVICE_COMMAND_META(DebugMonitorService, DisableDebug, FirmwareVersion_600), - - /* Atmosphere extensions. */ - MAKE_SERVICE_COMMAND_META(DebugMonitorService, AtmosphereGetProcessInfo), - MAKE_SERVICE_COMMAND_META(DebugMonitorService, AtmosphereGetCurrentLimitInfo), - }; -}; - -/* Represents deprecated DebugMonitorService (1.0.0-4.1.0). */ -class DebugMonitorServiceDeprecated : public DebugMonitorService { - private: - enum class CommandId { - GetUnknownStub = 0, - GetDebugProcessIds = 1, - LaunchDebugProcess = 2, - GetTitleProcessId = 3, - EnableDebugForTitleId = 4, - GetApplicationProcessId = 5, - EnableDebugForApplication = 6, - - AtmosphereGetProcessInfo = 65000, - AtmosphereGetCurrentLimitInfo = 65001, - }; - public: - DEFINE_SERVICE_DISPATCH_TABLE { - /* 1.0.0-4.1.0 */ - MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, GetUnknownStub), - MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, GetDebugProcessIds), - MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, LaunchDebugProcess), - MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, GetTitleProcessId), - MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, EnableDebugForTitleId), - MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, GetApplicationProcessId), - MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, EnableDebugForApplication), - - /* Atmosphere extensions. */ - MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, AtmosphereGetProcessInfo), - MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, AtmosphereGetCurrentLimitInfo), - }; -}; diff --git a/stratosphere/pm/source/pm_debug_monitor_service.cpp b/stratosphere/pm/source/pm_debug_monitor_service.cpp new file mode 100644 index 000000000..af4c6fa82 --- /dev/null +++ b/stratosphere/pm/source/pm_debug_monitor_service.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "pm_debug_monitor_service.hpp" +#include "impl/pm_process_manager.hpp" + +namespace sts::pm::dmnt { + + /* Actual command implementations. */ + Result DebugMonitorServiceBase::GetModuleIdList(Out out_count, OutBuffer out_buf, u64 unused) { + if (out_buf.num_elements > std::numeric_limits::max()) { + return ResultPmInvalidSize; + } + return impl::GetModuleIdList(out_count.GetPointer(), out_buf.buffer, out_buf.num_elements, unused); + } + + Result DebugMonitorServiceBase::GetExceptionProcessIdList(Out out_count, OutBuffer out_process_ids) { + if (out_process_ids.num_elements > std::numeric_limits::max()) { + return ResultPmInvalidSize; + } + return impl::GetExceptionProcessIdList(out_count.GetPointer(), out_process_ids.buffer, out_process_ids.num_elements); + } + + Result DebugMonitorServiceBase::StartProcess(u64 process_id) { + return impl::StartProcess(process_id); + } + + Result DebugMonitorServiceBase::GetProcessId(Out out, ncm::TitleId title_id) { + return impl::GetProcessId(out.GetPointer(), title_id); + } + + Result DebugMonitorServiceBase::HookToCreateProcess(Out out_hook, ncm::TitleId title_id) { + return impl::HookToCreateProcess(out_hook.GetHandlePointer(), title_id); + } + + Result DebugMonitorServiceBase::GetApplicationProcessId(Out out) { + return impl::GetApplicationProcessId(out.GetPointer()); + } + + Result DebugMonitorServiceBase::HookToCreateApplicationProcess(Out out_hook) { + return impl::HookToCreateApplicationProcess(out_hook.GetHandlePointer()); + } + + Result DebugMonitorServiceBase::ClearHook(u32 which) { + return impl::ClearHook(which); + } + + /* Atmosphere extension commands. */ + Result DebugMonitorServiceBase::AtmosphereGetProcessInfo(Out out_process_handle, Out out_loc, u64 process_id) { + return impl::AtmosphereGetProcessInfo(out_process_handle.GetHandlePointer(), out_loc.GetPointer(), process_id); + } + + Result DebugMonitorServiceBase::AtmosphereGetCurrentLimitInfo(Out out_cur_val, Out out_lim_val, u32 group, u32 resource) { + return impl::AtmosphereGetCurrentLimitInfo(out_cur_val.GetPointer(), out_lim_val.GetPointer(), group, resource); + } + +} diff --git a/stratosphere/pm/source/pm_debug_monitor_service.hpp b/stratosphere/pm/source/pm_debug_monitor_service.hpp new file mode 100644 index 000000000..88f7b2e75 --- /dev/null +++ b/stratosphere/pm/source/pm_debug_monitor_service.hpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include + +namespace sts::pm::dmnt { + + class DebugMonitorServiceBase : public IServiceObject { + protected: + /* Actual command implementations. */ + virtual Result GetModuleIdList(Out out_count, OutBuffer out_buf, u64 unused); + virtual Result GetExceptionProcessIdList(Out out_count, OutBuffer out_process_ids); + virtual Result StartProcess(u64 process_id); + virtual Result GetProcessId(Out out, ncm::TitleId title_id); + virtual Result HookToCreateProcess(Out out_hook, ncm::TitleId title_id); + virtual Result GetApplicationProcessId(Out out); + virtual Result HookToCreateApplicationProcess(Out out_hook); + virtual Result ClearHook(u32 which); + + /* Atmosphere extension commands. */ + virtual Result AtmosphereGetProcessInfo(Out out_process_handle, Out out_loc, u64 process_id); + virtual Result AtmosphereGetCurrentLimitInfo(Out out_cur_val, Out out_lim_val, u32 group, u32 resource); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* No entries, because DebugMonitorServiceBase is abstract. */ + }; + }; + + /* This represents modern DebugMonitorService (5.0.0+). */ + class DebugMonitorService final : public DebugMonitorServiceBase { + private: + enum class CommandId { + GetExceptionProcessIdList = 0, + StartProcess = 1, + GetProcessId = 2, + HookToCreateProcess = 3, + GetApplicationProcessId = 4, + HookToCreateApplicationProcess = 5, + + ClearHook = 6, + + AtmosphereGetProcessInfo = 65000, + AtmosphereGetCurrentLimitInfo = 65001, + }; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* 5.0.0-* */ + MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetExceptionProcessIdList), + MAKE_SERVICE_COMMAND_META(DebugMonitorService, StartProcess), + MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessId), + MAKE_SERVICE_COMMAND_META(DebugMonitorService, HookToCreateProcess), + MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetApplicationProcessId), + MAKE_SERVICE_COMMAND_META(DebugMonitorService, HookToCreateApplicationProcess), + + /* 6.0.0-* */ + MAKE_SERVICE_COMMAND_META(DebugMonitorService, ClearHook, FirmwareVersion_600), + + /* Atmosphere extensions. */ + MAKE_SERVICE_COMMAND_META(DebugMonitorService, AtmosphereGetProcessInfo), + MAKE_SERVICE_COMMAND_META(DebugMonitorService, AtmosphereGetCurrentLimitInfo), + }; + }; + + /* This represents deprecated DebugMonitorService (1.0.0-4.1.0). */ + class DebugMonitorServiceDeprecated final : public DebugMonitorServiceBase { + private: + enum class CommandId { + GetModuleIdList = 0, + GetExceptionProcessIdList = 1, + StartProcess = 2, + GetProcessId = 3, + HookToCreateProcess = 4, + GetApplicationProcessId = 5, + HookToCreateApplicationProcess = 6, + + AtmosphereGetProcessInfo = 65000, + AtmosphereGetCurrentLimitInfo = 65001, + }; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* 1.0.0-4.1.0 */ + MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, GetModuleIdList), + MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, GetExceptionProcessIdList), + MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, StartProcess), + MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, GetProcessId), + MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, HookToCreateProcess), + MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, GetApplicationProcessId), + MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, HookToCreateApplicationProcess), + + /* Atmosphere extensions. */ + MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, AtmosphereGetProcessInfo), + MAKE_SERVICE_COMMAND_META(DebugMonitorServiceDeprecated, AtmosphereGetCurrentLimitInfo), + }; + }; + +} diff --git a/stratosphere/pm/source/pm_info.cpp b/stratosphere/pm/source/pm_info.cpp deleted file mode 100644 index d40888225..000000000 --- a/stratosphere/pm/source/pm_info.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include - -#include "pm_registration.hpp" -#include "pm_info.hpp" - -Result InformationService::GetTitleId(Out tid, u64 pid) { - std::scoped_lock lk(Registration::GetProcessList()); - - std::shared_ptr proc = Registration::GetProcess(pid); - if (proc == NULL) { - return ResultPmProcessNotFound; - } - - tid.SetValue(proc->tid_sid.title_id); - return ResultSuccess; -} - -Result InformationService::AtmosphereGetProcessId(Out pid, u64 tid) { - std::scoped_lock lk(Registration::GetProcessList()); - - std::shared_ptr proc = Registration::GetProcessByTitleId(tid); - if (proc == nullptr) { - return ResultPmProcessNotFound; - } - - pid.SetValue(proc->pid); - return ResultSuccess; -} - -Result InformationService::AtmosphereHasLaunchedTitle(Out out, u64 tid) { - return sts::ldr::pm::HasLaunchedTitle(out.GetPointer(), sts::ncm::TitleId{tid}); -} diff --git a/stratosphere/pm/source/pm_info.hpp b/stratosphere/pm/source/pm_info.hpp deleted file mode 100644 index d45df54d6..000000000 --- a/stratosphere/pm/source/pm_info.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include -#include - -class InformationService final : public IServiceObject { - private: - enum class CommandId { - GetTitleId = 0, - AtmosphereGetProcessId = 65000, - AtmosphereHasLaunchedTitle = 65001, - }; - private: - /* Actual commands. */ - Result GetTitleId(Out tid, u64 pid); - - /* Atmosphere commands. */ - Result AtmosphereGetProcessId(Out pid, u64 tid); - Result AtmosphereHasLaunchedTitle(Out out, u64 tid); - public: - DEFINE_SERVICE_DISPATCH_TABLE { - MAKE_SERVICE_COMMAND_META(InformationService, GetTitleId), - MAKE_SERVICE_COMMAND_META(InformationService, AtmosphereGetProcessId), - MAKE_SERVICE_COMMAND_META(InformationService, AtmosphereHasLaunchedTitle), - }; -}; diff --git a/stratosphere/pm/source/pm_info_service.cpp b/stratosphere/pm/source/pm_info_service.cpp new file mode 100644 index 000000000..7d1b98d3d --- /dev/null +++ b/stratosphere/pm/source/pm_info_service.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "pm_info_service.hpp" +#include "impl/pm_process_manager.hpp" + +namespace sts::pm::info { + + /* Overrides for libstratosphere pm::info commands. */ + Result HasLaunchedTitle(bool *out, ncm::TitleId title_id) { + return ldr::pm::HasLaunchedTitle(out, title_id); + } + + /* Actual command implementations. */ + Result InformationService::GetTitleId(Out out, u64 process_id) { + return impl::GetTitleId(out.GetPointer(), process_id); + } + + /* Atmosphere extension commands. */ + Result InformationService::AtmosphereGetProcessId(Out out, ncm::TitleId title_id) { + return impl::GetProcessId(out.GetPointer(), title_id); + } + + Result InformationService::AtmosphereHasLaunchedTitle(Out out, ncm::TitleId title_id) { + return pm::info::HasLaunchedTitle(out.GetPointer(), title_id); + } + +} diff --git a/stratosphere/pm/source/pm_info_service.hpp b/stratosphere/pm/source/pm_info_service.hpp new file mode 100644 index 000000000..51e0d4f20 --- /dev/null +++ b/stratosphere/pm/source/pm_info_service.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include + +namespace sts::pm::info { + + class InformationService final : public IServiceObject { + private: + enum class CommandId { + GetTitleId = 0, + + AtmosphereGetProcessId = 65000, + AtmosphereHasLaunchedTitle = 65001, + }; + private: + /* Actual command implementations. */ + Result GetTitleId(Out out, u64 process_id); + + /* Atmosphere extension commands. */ + Result AtmosphereGetProcessId(Out out, ncm::TitleId title_id); + Result AtmosphereHasLaunchedTitle(Out out, ncm::TitleId title_id); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(InformationService, GetTitleId), + + MAKE_SERVICE_COMMAND_META(InformationService, AtmosphereGetProcessId), + MAKE_SERVICE_COMMAND_META(InformationService, AtmosphereHasLaunchedTitle), + }; + }; + +} diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp index c7115e610..d04c4e0a9 100644 --- a/stratosphere/pm/source/pm_main.cpp +++ b/stratosphere/pm/source/pm_main.cpp @@ -22,14 +22,15 @@ #include #include #include +#include #include -#include "pm_boot_mode.hpp" -#include "pm_info.hpp" -#include "pm_shell.hpp" -#include "pm_process_track.hpp" -#include "pm_registration.hpp" -#include "pm_debug_monitor.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" extern "C" { extern u32 __start__; @@ -56,7 +57,6 @@ void __libnx_exception_handler(ThreadExceptionDump *ctx) { StratosphereCrashHandler(ctx); } - void __libnx_initheap(void) { void* addr = nx_inner_heap; size_t size = nx_inner_heap_size; @@ -69,30 +69,35 @@ void __libnx_initheap(void) { fake_heap_end = (char*)addr + size; } -void RegisterPrivilegedProcessesWithFs() { - /* Ensures that all privileged processes are registered with full FS permissions. */ - constexpr u64 PRIVILEGED_PROCESS_MIN = 0; - constexpr u64 PRIVILEGED_PROCESS_MAX = 0x4F; +namespace { - const u32 PRIVILEGED_FAH[0x1C/sizeof(u32)] = {0x00000001, 0x00000000, 0x80000000, 0x0000001C, 0x00000000, 0x0000001C, 0x00000000}; - const u32 PRIVILEGED_FAC[0x2C/sizeof(u32)] = {0x00000001, 0x00000000, 0x80000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF}; + static constexpr u32 PrivilegedFileAccessHeader[0x1C / sizeof(u32)] = {0x00000001, 0x00000000, 0x80000000, 0x0000001C, 0x00000000, 0x0000001C, 0x00000000}; + static constexpr u32 PrivilegedFileAccessControl[0x2C / sizeof(u32)] = {0x00000001, 0x00000000, 0x80000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF}; + static constexpr size_t ProcessCountMax = 0x40; - u32 num_pids; - u64 pids[PRIVILEGED_PROCESS_MAX+1]; - if (R_SUCCEEDED(svcGetProcessList(&num_pids, pids, sizeof(pids)/sizeof(pids[0])))) { - for (u32 i = 0; i < num_pids; i++) { - const u64 pid = pids[i]; - if (PRIVILEGED_PROCESS_MIN <= pid && pid <= PRIVILEGED_PROCESS_MAX) { - fsprUnregisterProgram(pid); - fsprRegisterProgram(pid, pid, FsStorageId_NandSystem, PRIVILEGED_FAH, sizeof(PRIVILEGED_FAH), PRIVILEGED_FAC, sizeof(PRIVILEGED_FAC)); + /* 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. */ + void RegisterPrivilegedProcessWithFs(u64 process_id) { + fsprUnregisterProgram(process_id); + fsprRegisterProgram(process_id, process_id, FsStorageId_NandSystem, PrivilegedFileAccessHeader, sizeof(PrivilegedFileAccessHeader), PrivilegedFileAccessControl, sizeof(PrivilegedFileAccessControl)); + } + + void RegisterPrivilegedProcessesWithFs() { + /* Get privileged process range. */ + u64 min_priv_process_id = 0, max_priv_process_id = 0; + sts::cfg::GetInitialProcessRange(&min_priv_process_id, &max_priv_process_id); + + /* Get list of processes, register all privileged ones. */ + u32 num_pids; + u64 pids[ProcessCountMax]; + R_ASSERT(svcGetProcessList(&num_pids, pids, ProcessCountMax)); + for (size_t i = 0; i < num_pids; i++) { + if (min_priv_process_id <= pids[i] && pids[i] <= max_priv_process_id) { + RegisterPrivilegedProcessWithFs(pids[i]); } } - } else { - for (u64 pid = PRIVILEGED_PROCESS_MIN; pid <= PRIVILEGED_PROCESS_MAX; pid++) { - fsprUnregisterProgram(pid); - fsprRegisterProgram(pid, pid, FsStorageId_NandSystem, PRIVILEGED_FAH, sizeof(PRIVILEGED_FAH), PRIVILEGED_FAC, sizeof(PRIVILEGED_FAC)); - } } + } void __appInit(void) { @@ -120,37 +125,34 @@ void __appInit(void) { void __appExit(void) { /* Cleanup services. */ fsdevUnmountAll(); - splExit(); - smManagerExit(); - ldrPmExit(); - fsprExit(); - lrExit(); fsExit(); + splExit(); + ldrPmExit(); + lrExit(); + smManagerExit(); + fsprExit(); } int main(int argc, char **argv) { - HosThread process_track_thread; - consoleDebugInit(debugDevice_SVC); - - /* Initialize and spawn the Process Tracking thread. */ - Registration::InitializeSystemResources(); - R_ASSERT(process_track_thread.Initialize(&ProcessTracking::MainLoop, NULL, 0x4000, 0x15)); - R_ASSERT(process_track_thread.Start()); + /* Initialize process manager implementation. */ + R_ASSERT(sts::pm::impl::InitializeProcessManager()); /* Create Server Manager. */ static auto s_server_manager = WaitableManager(1); - /* TODO: Create services. */ - if (GetRuntimeFirmwareVersion() <= FirmwareVersion_400) { - s_server_manager.AddWaitable(new ServiceServer("pm:shell", 3)); - s_server_manager.AddWaitable(new ServiceServer("pm:dmnt", 3)); + /* Create Services. */ + /* NOTE: Extra sessions have been added to pm:bm and pm:info to facilitate access by the rest of stratosphere. */ + /* Also Note: PM was rewritten in 5.0.0, so the shell and dmnt services are different before/after. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + s_server_manager.AddWaitable(new ServiceServer("pm:shell", 3)); + s_server_manager.AddWaitable(new ServiceServer("pm:dmnt", 3)); } else { - s_server_manager.AddWaitable(new ServiceServer("pm:shell", 3)); - s_server_manager.AddWaitable(new ServiceServer("pm:dmnt", 3)); + s_server_manager.AddWaitable(new ServiceServer("pm:shell", 3)); + s_server_manager.AddWaitable(new ServiceServer("pm:dmnt", 3)); } - s_server_manager.AddWaitable(new ServiceServer("pm:bm", 6)); - s_server_manager.AddWaitable(new ServiceServer("pm:info", 19)); + s_server_manager.AddWaitable(new ServiceServer("pm:bm", 6)); + s_server_manager.AddWaitable(new ServiceServer("pm:info", 19)); /* Loop forever, servicing our services. */ s_server_manager.Process(); diff --git a/stratosphere/pm/source/pm_process_track.cpp b/stratosphere/pm/source/pm_process_track.cpp deleted file mode 100644 index 797b4371c..000000000 --- a/stratosphere/pm/source/pm_process_track.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include "pm_process_track.hpp" -#include "pm_registration.hpp" - -void ProcessTracking::MainLoop(void *arg) { - /* Make a new waitable manager. */ - static auto s_process_waiter = WaitableManager(1); - s_process_waiter.AddWaitable(Registration::GetProcessLaunchStartEvent()); - - /* Service processes. */ - s_process_waiter.Process(); -} \ No newline at end of file diff --git a/stratosphere/pm/source/pm_process_wait.hpp b/stratosphere/pm/source/pm_process_wait.hpp deleted file mode 100644 index 1630a5518..000000000 --- a/stratosphere/pm/source/pm_process_wait.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include -#include -#include -#include "pm_registration.hpp" - -class ProcessWaiter final : public IWaitable { - public: - std::shared_ptr process; - - ProcessWaiter(std::shared_ptr p) : process(p) { - /* ... */ - } - - std::shared_ptr GetProcess() { - return this->process; - } - - /* IWaitable */ - Handle GetHandle() override { - return this->process->handle; - } - - Result HandleSignaled(u64 timeout) override { - return Registration::HandleSignaledProcess(this->GetProcess()); - } -}; - -class ProcessList final { - private: - HosRecursiveMutex m; - - HosRecursiveMutex *GetMutex() { - return &this->m; - } - public: - std::vector> processes; - - void lock() { - GetMutex()->lock(); - } - - void unlock() { - GetMutex()->unlock(); - } - - void try_lock() { - GetMutex()->try_lock(); - } -}; - diff --git a/stratosphere/pm/source/pm_registration.cpp b/stratosphere/pm/source/pm_registration.cpp deleted file mode 100644 index fd572272e..000000000 --- a/stratosphere/pm/source/pm_registration.cpp +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include - -#include "pm_registration.hpp" -#include "pm_resource_limits.hpp" - -static ProcessList g_process_list; -static ProcessList g_dead_process_list; - -static IEvent *g_process_launch_start_event = nullptr; -static HosSignal g_finish_launch_signal; - -static HosMutex g_process_launch_mutex; -static Registration::ProcessLaunchState g_process_launch_state; - -static std::atomic_bool g_debug_next_application(false); -static std::atomic g_debug_on_launch_tid(0); - -static IEvent *g_process_event = nullptr; -static IEvent *g_debug_title_event = nullptr; -static IEvent *g_debug_application_event = nullptr; -static IEvent *g_boot_finished_event = nullptr; - -ProcessList &Registration::GetProcessList() { - return g_process_list; -} - -void Registration::InitializeSystemResources() { - g_process_event = CreateWriteOnlySystemEvent(); - g_debug_title_event = CreateWriteOnlySystemEvent(); - g_debug_application_event = CreateWriteOnlySystemEvent(); - g_boot_finished_event = CreateWriteOnlySystemEvent(); - - /* Auto-clear non-system event. */ - g_process_launch_start_event = CreateHosEvent(&Registration::ProcessLaunchStartCallback); - - ResourceLimitUtils::InitializeLimits(); -} - -Result Registration::ProcessLaunchStartCallback(u64 timeout) { - g_process_launch_start_event->Clear(); - Registration::HandleProcessLaunch(); - return ResultSuccess; -} - -IWaitable *Registration::GetProcessLaunchStartEvent() { - return g_process_launch_start_event; -} - -Result Registration::LaunchProcess(u64 *out_pid, const TidSid tid_sid, const u64 launch_flags) { - LoaderProgramInfo program_info = {0}; - Process new_process = {0}; - new_process.tid_sid = tid_sid; - - /* Clear, get accessors for access controls. */ - static u8 s_ac_buf[4 * sizeof(LoaderProgramInfo)]; - std::memset(s_ac_buf, 0xCC, sizeof(s_ac_buf)); - u8 *acid_sac = s_ac_buf, *aci0_sac = acid_sac + sizeof(LoaderProgramInfo), *fac = aci0_sac + sizeof(LoaderProgramInfo), *fah = fac + sizeof(LoaderProgramInfo); - - /* Check that this is a real program. */ - R_TRY(ldrPmGetProgramInfo(new_process.tid_sid.title_id, new_process.tid_sid.storage_id, &program_info)); - - /* Get the resource limit handle, ensure that we can launch the program. */ - if ((program_info.application_type & 3) == 1 && HasApplicationProcess()) { - return ResultPmApplicationRunning; - } - - /* Try to register the title for launch in loader... */ - R_TRY(ldrPmRegisterTitle(new_process.tid_sid.title_id, new_process.tid_sid.storage_id, &new_process.ldr_queue_index)); - auto ldr_register_guard = SCOPE_GUARD { - ldrPmUnregisterTitle(new_process.ldr_queue_index); - }; - - /* Make sure the previous application is cleaned up. */ - if ((program_info.application_type & 3) == 1) { - ResourceLimitUtils::EnsureApplicationResourcesAvailable(); - } - - /* Try to create the process... */ - R_TRY(ldrPmCreateProcess(LAUNCHFLAGS_ARGLOW(launch_flags) | LAUNCHFLAGS_ARGHIGH(launch_flags), new_process.ldr_queue_index, ResourceLimitUtils::GetResourceLimitHandle(program_info.application_type), &new_process.handle)); - auto proc_create_guard = SCOPE_GUARD { - svcCloseHandle(new_process.handle); - new_process.handle = 0; - }; - - /* Get the new process's id. */ - R_ASSERT(svcGetProcessId(&new_process.pid, new_process.handle)); - - /* Register with FS. */ - memcpy(fac, program_info.ac_buffer + program_info.acid_sac_size + program_info.aci0_sac_size, program_info.acid_fac_size); - memcpy(fah, program_info.ac_buffer + program_info.acid_sac_size + program_info.aci0_sac_size + program_info.acid_fac_size, program_info.aci0_fah_size); - R_TRY(fsprRegisterProgram(new_process.pid, new_process.tid_sid.title_id, new_process.tid_sid.storage_id, fah, program_info.aci0_fah_size, fac, program_info.acid_fac_size)); - auto fs_register_guard = SCOPE_GUARD { - fsprUnregisterProgram(new_process.pid); - }; - - /* Register with SM. */ - memcpy(acid_sac, program_info.ac_buffer, program_info.acid_sac_size); - memcpy(aci0_sac, program_info.ac_buffer + program_info.acid_sac_size, program_info.aci0_sac_size); - R_TRY(smManagerRegisterProcess(new_process.pid, acid_sac, program_info.acid_sac_size, aci0_sac, program_info.aci0_sac_size)); - auto sm_register_guard = SCOPE_GUARD { - smManagerUnregisterProcess(new_process.pid); - }; - - /* Setup process flags. */ - if (program_info.application_type & 1) { - new_process.flags |= PROCESSFLAGS_APPLICATION; - } - if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_200) && LAUNCHFLAGS_NOTIYDEBUGSPECIAL(launch_flags) && (program_info.application_type & 4)) { - new_process.flags |= PROCESSFLAGS_NOTIFYDEBUGSPECIAL; - } - if (LAUNCHFLAGS_NOTIFYWHENEXITED(launch_flags)) { - new_process.flags |= PROCESSFLAGS_NOTIFYWHENEXITED; - } - if (LAUNCHFLAGS_NOTIFYDEBUGEVENTS(launch_flags) && (GetRuntimeFirmwareVersion() < FirmwareVersion_200 || (program_info.application_type & 4))) { - new_process.flags |= PROCESSFLAGS_NOTIFYDEBUGEVENTS; - } - - /* Add process to the list. */ - Registration::AddProcessToList(std::make_shared(new_process)); - auto reg_list_guard = SCOPE_GUARD { - Registration::RemoveProcessFromList(new_process.pid); - }; - - /* Signal, if relevant. */ - if (new_process.tid_sid.title_id == g_debug_on_launch_tid.load()) { - g_debug_title_event->Signal(); - g_debug_on_launch_tid = 0; - } else if ((new_process.flags & PROCESSFLAGS_APPLICATION) && g_debug_next_application.load()) { - g_debug_application_event->Signal(); - g_debug_next_application = false; - } else if (!(LAUNCHFLAGS_STARTSUSPENDED(launch_flags))) { - R_TRY(svcStartProcess(new_process.handle, program_info.main_thread_priority, program_info.default_cpu_id, program_info.main_thread_stack_size)); - SetProcessState(new_process.pid, ProcessState_Running); - } - - /* Dismiss scope guards. */ - reg_list_guard.Cancel(); - sm_register_guard.Cancel(); - fs_register_guard.Cancel(); - proc_create_guard.Cancel(); - ldr_register_guard.Cancel(); - - /* Set output pid. */ - *out_pid = new_process.pid; - - return ResultSuccess; -} - -void Registration::HandleProcessLaunch() { - /* Actually launch process. */ - g_process_launch_state.result = LaunchProcess(g_process_launch_state.out_pid, g_process_launch_state.tid_sid, g_process_launch_state.launch_flags); - /* Signal to waiting thread. */ - g_finish_launch_signal.Signal(); -} - - -Result Registration::LaunchDebugProcess(u64 pid) { - std::scoped_lock lk(GetProcessList()); - LoaderProgramInfo program_info = {0}; - - std::shared_ptr proc = GetProcess(pid); - if (proc == NULL) { - return ResultPmProcessNotFound; - } - - if (proc->state >= ProcessState_Running) { - return ResultPmAlreadyStarted; - } - - /* Check that this is a real program. */ - R_TRY(ldrPmGetProgramInfo(proc->tid_sid.title_id, proc->tid_sid.storage_id, &program_info)); - - /* Actually start the process. */ - R_TRY(svcStartProcess(proc->handle, program_info.main_thread_priority, program_info.default_cpu_id, program_info.main_thread_stack_size)); - - proc->state = ProcessState_Running; - return ResultSuccess; -} - -Result Registration::LaunchProcess(u64 title_id, FsStorageId storage_id, u64 launch_flags, u64 *out_pid) { - /* Only allow one mutex to exist. */ - std::scoped_lock lk{g_process_launch_mutex}; - g_process_launch_state.tid_sid.title_id = title_id; - g_process_launch_state.tid_sid.storage_id = storage_id; - g_process_launch_state.launch_flags = launch_flags; - g_process_launch_state.out_pid = out_pid; - - /* Start a launch, and wait for it to exit. */ - g_finish_launch_signal.Reset(); - g_process_launch_start_event->Signal(); - g_finish_launch_signal.Wait(); - - return g_process_launch_state.result; -} - -Result Registration::LaunchProcessByTidSid(TidSid tid_sid, u64 launch_flags, u64 *out_pid) { - return LaunchProcess(tid_sid.title_id, tid_sid.storage_id, launch_flags, out_pid); -}; - -Result Registration::HandleSignaledProcess(std::shared_ptr process) { - u64 tmp; - - /* Reset the signal. */ - svcResetSignal(process->handle); - - ProcessState old_state; - old_state = process->state; - svcGetProcessInfo(&tmp, process->handle, ProcessInfoType_ProcessState); - process->state = (ProcessState)tmp; - - if (old_state == ProcessState_Crashed && process->state != ProcessState_Crashed) { - process->flags &= ~PROCESSFLAGS_CRASH_DEBUG; - } - switch (process->state) { - case ProcessState_Created: - case ProcessState_CreatedAttached: - case ProcessState_Exiting: - break; - case ProcessState_Running: - if (process->flags & PROCESSFLAGS_NOTIFYDEBUGEVENTS) { - process->flags &= ~(PROCESSFLAGS_DEBUGEVENTPENDING | PROCESSFLAGS_DEBUGSUSPENDED); - process->flags |= PROCESSFLAGS_DEBUGEVENTPENDING; - g_process_event->Signal(); - } - if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_200) && process->flags & PROCESSFLAGS_NOTIFYDEBUGSPECIAL) { - process->flags &= ~(PROCESSFLAGS_NOTIFYDEBUGSPECIAL | PROCESSFLAGS_DEBUGDETACHED); - process->flags |= PROCESSFLAGS_DEBUGDETACHED; - } - break; - case ProcessState_Crashed: - process->flags |= (PROCESSFLAGS_CRASHED | PROCESSFLAGS_CRASH_DEBUG); - g_process_event->Signal(); - break; - case ProcessState_RunningAttached: - if (process->flags & PROCESSFLAGS_NOTIFYDEBUGEVENTS) { - process->flags &= ~(PROCESSFLAGS_DEBUGEVENTPENDING | PROCESSFLAGS_DEBUGSUSPENDED); - process->flags |= PROCESSFLAGS_DEBUGEVENTPENDING; - g_process_event->Signal(); - } - break; - case ProcessState_Exited: - if (process->flags & PROCESSFLAGS_NOTIFYWHENEXITED && GetRuntimeFirmwareVersion() < FirmwareVersion_500) { - g_process_event->Signal(); - } else { - FinalizeExitedProcess(process); - } - return ResultKernelConnectionClosed; - case ProcessState_DebugSuspended: - if (process->flags & PROCESSFLAGS_NOTIFYDEBUGEVENTS) { - process->flags |= (PROCESSFLAGS_DEBUGEVENTPENDING | PROCESSFLAGS_DEBUGSUSPENDED); - g_process_event->Signal(); - } - break; - } - return ResultSuccess; -} - -void Registration::FinalizeExitedProcess(std::shared_ptr process) { - bool signal_debug_process_5x; - - /* Scope to manage process list lock. */ - { - std::scoped_lock lk(GetProcessList()); - - signal_debug_process_5x = (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) && process->flags & PROCESSFLAGS_NOTIFYWHENEXITED; - - /* Unregister with FS, SM, and LDR. */ - R_ASSERT(fsprUnregisterProgram(process->pid)); - R_ASSERT(smManagerUnregisterProcess(process->pid)); - R_ASSERT(ldrPmUnregisterTitle(process->ldr_queue_index)); - - /* Close the process's handle. */ - svcCloseHandle(process->handle); - process->handle = 0; - - /* Insert into dead process list, if relevant. */ - if (signal_debug_process_5x) { - std::scoped_lock dead_lk(g_dead_process_list); - - g_dead_process_list.processes.push_back(process); - } - - /* Remove NOTE: This probably frees process. */ - RemoveProcessFromList(process->pid); - } - - if (signal_debug_process_5x) { - g_process_event->Signal(); - } -} - -void Registration::AddProcessToList(std::shared_ptr process) { - std::scoped_lock lk(GetProcessList()); - - g_process_list.processes.push_back(process); - g_process_launch_start_event->GetManager()->AddWaitable(new ProcessWaiter(process)); -} - -void Registration::RemoveProcessFromList(u64 pid) { - std::scoped_lock lk(GetProcessList()); - - /* Remove process from list. */ - for (unsigned int i = 0; i < g_process_list.processes.size(); i++) { - std::shared_ptr process = g_process_list.processes[i]; - if (process->pid == pid) { - g_process_list.processes.erase(g_process_list.processes.begin() + i); - svcCloseHandle(process->handle); - process->handle = 0; - break; - } - } -} - -void Registration::SetProcessState(u64 pid, ProcessState new_state) { - std::scoped_lock lk(GetProcessList()); - - /* Set process state. */ - for (auto &process : g_process_list.processes) { - if (process->pid == pid) { - process->state = new_state; - break; - } - } -} - -bool Registration::HasApplicationProcess(std::shared_ptr *out) { - std::scoped_lock lk(GetProcessList()); - - for (auto &process : g_process_list.processes) { - if (process->flags & PROCESSFLAGS_APPLICATION) { - if (out != nullptr) { - *out = process; - } - return true; - } - } - - return false; -} - -std::shared_ptr Registration::GetProcess(u64 pid) { - std::scoped_lock lk(GetProcessList()); - - for (auto &process : g_process_list.processes) { - if (process->pid == pid) { - return process; - } - } - - return nullptr; -} - -std::shared_ptr Registration::GetProcessByTitleId(u64 tid) { - std::scoped_lock lk(GetProcessList()); - - for (auto &process : g_process_list.processes) { - if (process->tid_sid.title_id == tid) { - return process; - } - } - - return nullptr; -} - - -Result Registration::GetDebugProcessIds(u64 *out_pids, u32 max_out, u32 *num_out) { - std::scoped_lock lk(GetProcessList()); - u32 num = 0; - - - for (auto &process : g_process_list.processes) { - if (process->flags & PROCESSFLAGS_CRASH_DEBUG && num < max_out) { - out_pids[num++] = process->pid; - } - } - - *num_out = num; - return ResultSuccess; -} - -Handle Registration::GetProcessEventHandle() { - return g_process_event->GetHandle(); -} - -void Registration::GetProcessEventType(u64 *out_pid, u64 *out_type) { - /* Scope to manage process list lock. */ - { - std::scoped_lock lk(GetProcessList()); - - for (auto &p : g_process_list.processes) { - if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_200) && p->state >= ProcessState_Running && p->flags & PROCESSFLAGS_DEBUGDETACHED) { - p->flags &= ~PROCESSFLAGS_DEBUGDETACHED; - *out_pid = p->pid; - *out_type = (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) ? PROCESSEVENTTYPE_500_DEBUGDETACHED : PROCESSEVENTTYPE_DEBUGDETACHED; - return; - } - if (p->flags & PROCESSFLAGS_DEBUGEVENTPENDING) { - u64 old_flags = p->flags; - p->flags &= ~PROCESSFLAGS_DEBUGEVENTPENDING; - *out_pid = p->pid; - *out_type = (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) ? - ((old_flags & PROCESSFLAGS_DEBUGSUSPENDED) ? - PROCESSEVENTTYPE_500_SUSPENDED : - PROCESSEVENTTYPE_500_RUNNING) : - ((old_flags & PROCESSFLAGS_DEBUGSUSPENDED) ? - PROCESSEVENTTYPE_SUSPENDED : - PROCESSEVENTTYPE_RUNNING); - return; - } - if (p->flags & PROCESSFLAGS_CRASHED) { - *out_pid = p->pid; - *out_type = (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) ? PROCESSEVENTTYPE_500_CRASH : PROCESSEVENTTYPE_CRASH; - return; - } - if (GetRuntimeFirmwareVersion() < FirmwareVersion_500 && p->flags & PROCESSFLAGS_NOTIFYWHENEXITED && p->state == ProcessState_Exited) { - *out_pid = p->pid; - *out_type = PROCESSEVENTTYPE_EXIT; - return; - } - } - - *out_pid = 0; - *out_type = 0; - } - - if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_500)) { - std::scoped_lock dead_lk(g_dead_process_list); - - if (g_dead_process_list.processes.size()) { - std::shared_ptr process = g_dead_process_list.processes[0]; - g_dead_process_list.processes.erase(g_dead_process_list.processes.begin()); - *out_pid = process->pid; - *out_type = PROCESSEVENTTYPE_500_EXIT; - return; - } - } -} - - -Result Registration::EnableDebugForTitleId(u64 tid, Handle *out) { - u64 old = g_debug_on_launch_tid.exchange(tid); - if (old) { - g_debug_on_launch_tid = old; - return ResultPmDebugHookInUse; - } - *out = g_debug_title_event->GetHandle(); - return ResultSuccess; -} - -Result Registration::EnableDebugForApplication(Handle *out) { - g_debug_next_application = true; - *out = g_debug_application_event->GetHandle(); - return ResultSuccess; -} - -Result Registration::DisableDebug(u32 which) { - if (which & 1) { - g_debug_on_launch_tid = 0; - } - if (which & 2) { - g_debug_next_application = false; - } - return ResultSuccess; -} - -Handle Registration::GetDebugTitleEventHandle() { - return g_debug_title_event->GetHandle(); -} - -Handle Registration::GetDebugApplicationEventHandle() { - return g_debug_application_event->GetHandle(); -} - -Handle Registration::GetBootFinishedEventHandle() { - return g_boot_finished_event->GetHandle(); -} - -bool Registration::HasLaunchedTitle(u64 title_id) { - bool has_launched = false; - R_ASSERT(sts::ldr::pm::HasLaunchedTitle(&has_launched, sts::ncm::TitleId{title_id})); - return has_launched; -} - -void Registration::SignalBootFinished() { - g_boot_finished_event->Signal(); -} diff --git a/stratosphere/pm/source/pm_registration.hpp b/stratosphere/pm/source/pm_registration.hpp deleted file mode 100644 index d8efcf529..000000000 --- a/stratosphere/pm/source/pm_registration.hpp +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include -#include -#include -#include - -class ProcessList; - -#define LAUNCHFLAGS_NOTIFYWHENEXITED(flags) (flags & 1) -#define LAUNCHFLAGS_STARTSUSPENDED(flags) (flags & (GetRuntimeFirmwareVersion() >= FirmwareVersion_500 ? 0x10 : 0x2)) -#define LAUNCHFLAGS_ARGLOW(flags) (GetRuntimeFirmwareVersion() >= FirmwareVersion_500 ? ((flags & 0x14) != 0x10) : (GetRuntimeFirmwareVersion() >= FirmwareVersion_200 ? ((flags & 0x6) != 0x2) : ((flags >> 2) & 1))) -#define LAUNCHFLAGS_ARGHIGH(flags) ((flags & (GetRuntimeFirmwareVersion() >= FirmwareVersion_500 ? 0x20 : 0x8)) ? 2 : 0) -#define LAUNCHFLAGS_NOTIFYDEBUGEVENTS(flags) (flags & (GetRuntimeFirmwareVersion() >= FirmwareVersion_500 ? 0x8 : 0x10)) -#define LAUNCHFLAGS_NOTIYDEBUGSPECIAL(flags) (flags & (GetRuntimeFirmwareVersion() >= FirmwareVersion_500 ? 0x2 : (GetRuntimeFirmwareVersion() >= FirmwareVersion_200 ? 0x20 : 0x0))) - -// none of these names are official or authoritative in any way -enum { - /* - set in HandleProcessLaunch when - - launch_flags has LAUNCHFLAGS_NOTIFYWHENEXITED set - signals g_process_event in HandleSignaledProcess when - - process enters Exited state - - we're below 5.0.0 - (finalizes dead process when not set) - adds to dead process list in FinalizeExitedProcess when - - we're 5.0.0+ - (also signals g_process_event) - [5.0.0+] causes ProcessEventType 2 - */ - PROCESSFLAGS_NOTIFYWHENEXITED = 0x001, - - /* - set in HandleSignaledProcess when - - process crashes - causes ProcessEventType 1 ([5.0.0+] 3) (persistent) - */ - PROCESSFLAGS_CRASHED = 0x002, - - /* - cleared in HandleSignaledProcess when - - process goes from CRASHED to any other state - set in HandleSignaledProcess when - - process crashes - adds process to GetDebugProcessIds - */ - PROCESSFLAGS_CRASH_DEBUG = 0x004, - - /* - set in HandleProcessLaunch when - - launch_flags has LAUNCHFLAGS_NOTIFYDEBUGEVENTS set - - and (we're 1.0.0 or program_info.application_type & 4) (is this because 1.0.0 doesn't check?) - signals g_process_event in HandleSignaledProcess when - - process enters DebugDetached state - signals g_process_event in HandleSignaledProcess when - - process enters Running state - */ - PROCESSFLAGS_NOTIFYDEBUGEVENTS = 0x008, - - /* - set in HandleSignaledProcess when - - process enters DebugDetached state - - and PROCESSFLAGS_NOTIFYDEBUGEVENTS is set - set in HandleSignaledProcess when - - process enters Running state - - and PROCESSFLAGS_NOTIFYDEBUGEVENTS is set - set in HandleSignaledProcess when - - process enters DebugSuspended state - - and PROCESSFLAGS_NOTIFYDEBUGEVENTS is set - causes some complicated ProcessEvent (one-shot) - */ - PROCESSFLAGS_DEBUGEVENTPENDING = 0x010, - - /* - cleared in HandleSignaledProcess when - - process enters DebugDetached state - - and PROCESSFLAGS_NOTIFYDEBUGEVENTS is set - cleared in HandleSignaledProcess when - - process enters Running state - - and PROCESSFLAGS_NOTIFYDEBUGEVENTS is set - set in HandleSignaledProcess when - - process enters DebugSuspended state - - and PROCESSFLAGS_NOTIFYDEBUGEVENTS is set - */ - PROCESSFLAGS_DEBUGSUSPENDED = 0x020, - - /* - set in HandleProcessLaunch when - - program_info.application_type & 1 - signals g_debug_application_event in HandleProcessLaunch when - - g_debug_next_application is set (unsets g_debug_next_application) - causes HasApplicationProcess to return true - meaning? - application - */ - PROCESSFLAGS_APPLICATION = 0x040, - - /* - set in HandleProcessLaunch when - - we're above 2.0.0 (creport related?) - - and launch_flags has LAUNCHFLAGS_NOTIFYDEBUGSPECIAL set - - and program_info.application_type & 4 - tested in HandleSignaledProcess when - - process enters DebugDetached state - - and we're above 2.0.0 - causes - - clearing of PROCESSFLAGS_NOTIFYDEBUGSPECIAL (one-shot?) - - setting of PROCESSFLAGS_DEBUGDETACHED - */ - PROCESSFLAGS_NOTIFYDEBUGSPECIAL = 0x080, - - /* - set in HandleSignaledProcess when - - process enters DebugDetached state - - and we're above 2.0.0 - - and PROCESSFLAGS_NOTIFYDEBUGSPECIAL was set - causes ProcessEventType 5 ([5.0.0+] 2) (one-shot) - */ - PROCESSFLAGS_DEBUGDETACHED = 0x100, -}; - -enum { - PROCESSEVENTTYPE_CRASH = 1, - PROCESSEVENTTYPE_EXIT = 2, // only fired once, when process enters DebugDetached state (likely creport related) - PROCESSEVENTTYPE_RUNNING = 3, // debug detached or running - PROCESSEVENTTYPE_SUSPENDED = 4, // debug suspended - PROCESSEVENTTYPE_DEBUGDETACHED = 5, - - - PROCESSEVENTTYPE_500_EXIT = 1, - PROCESSEVENTTYPE_500_DEBUGDETACHED = 2, // only fired once, when process enters DebugDetached state (likely creport related) - PROCESSEVENTTYPE_500_CRASH = 3, - PROCESSEVENTTYPE_500_RUNNING = 4, // debug detached or running - PROCESSEVENTTYPE_500_SUSPENDED = 5, // debug suspended -}; - -class Registration { - public: - struct TidSid { - u64 title_id; - FsStorageId storage_id; - }; - struct Process { - Handle handle; - u64 pid; - u64 ldr_queue_index; - Registration::TidSid tid_sid; - ProcessState state; - u32 flags; - }; - - struct ProcessLaunchState { - TidSid tid_sid; - u64 launch_flags; - u64* out_pid; - Result result; - }; - private: - static Result LaunchProcess(u64 *out_pid, const TidSid tid_sid, const u64 launch_flags); - public: - /* TODO: Better evaluate public vs private API. */ - static void InitializeSystemResources(); - static IWaitable *GetProcessLaunchStartEvent(); - static ProcessList &GetProcessList(); - static Result ProcessLaunchStartCallback(u64 timeout); - - static Result HandleSignaledProcess(std::shared_ptr process); - static void FinalizeExitedProcess(std::shared_ptr process); - - static void AddProcessToList(std::shared_ptr process); - static void RemoveProcessFromList(u64 pid); - static void SetProcessState(u64 pid, ProcessState new_state); - - static std::shared_ptr GetProcess(u64 pid); - static std::shared_ptr GetProcessByTitleId(u64 tid); - static Result GetDebugProcessIds(u64 *out_pids, u32 max_out, u32 *num_out); - static Handle GetProcessEventHandle(); - static void GetProcessEventType(u64 *out_pid, u64 *out_type); - static Result EnableDebugForTitleId(u64 tid, Handle *out); - static Result EnableDebugForApplication(Handle *out); - static Result DisableDebug(u32 which); - static Handle GetDebugTitleEventHandle(); - static Handle GetDebugApplicationEventHandle(); - static Handle GetBootFinishedEventHandle(); - - static void HandleProcessLaunch(); - static Result LaunchDebugProcess(u64 pid); - static void SignalFinishLaunchProcess(); - static Result LaunchProcess(u64 title_id, FsStorageId storage_id, u64 launch_flags, u64 *out_pid); - static Result LaunchProcessByTidSid(TidSid tid_sid, u64 launch_flags, u64 *out_pid); - - static bool HasLaunchedTitle(u64 title_id); - - static void SignalBootFinished(); - - static bool HasApplicationProcess(std::shared_ptr *out = nullptr); -}; - -#include "pm_process_wait.hpp" diff --git a/stratosphere/pm/source/pm_resource_limits.cpp b/stratosphere/pm/source/pm_resource_limits.cpp deleted file mode 100644 index f76cbb059..000000000 --- a/stratosphere/pm/source/pm_resource_limits.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "pm_resource_limits.hpp" - -/* Give 24 MiB to SYSTEM from Applet. This allows for more custom sysmodules. */ -#define ATMOSPHERE_EXTRA_SYSTEM_MEMORY_FOR_SYSMODULES 0x1800000ULL - -/* Memory that system, application, applet are allowed to allocate. */ -static const u64 g_memory_resource_limits_deprecated[5][3] = { - {0x010D00000ULL, 0x0CD500000ULL, 0x021700000ULL}, - {0x01E100000ULL, 0x080000000ULL, 0x061800000ULL}, - {0x014800000ULL, 0x0CD500000ULL, 0x01DC00000ULL}, - {0x028D00000ULL, 0x133400000ULL, 0x023800000ULL}, - {0x028D00000ULL, 0x0CD500000ULL, 0x089700000ULL} -}; - -/* These limits were altered in 4.x, which introduced ResourceLimitUtils::BoostSystemMemoryResourceLimit(). */ -static const u64 g_memory_resource_limits_4x[5][3] = { - {0x011700000ULL, 0x0CD500000ULL, 0x020D00000ULL}, /* 10 MB was taken from applet and given to system. */ - {0x01E100000ULL, 0x080000000ULL, 0x061800000ULL}, /* No changes. */ - {0x015200000ULL, 0x0CD500000ULL, 0x01D200000ULL}, /* 10 MB was taken from applet and given to system. */ - {0x028D00000ULL, 0x133400000ULL, 0x023800000ULL}, /* No changes. */ - {0x028D00000ULL, 0x0CD500000ULL, 0x089700000ULL} /* No changes. */ -}; - -/* These limits were altered again in 5.x. */ -static const u64 g_memory_resource_limits_5x[5][3] = { - {0x012300000ULL, 0x0CD500000ULL, 0x020100000ULL}, /* 12 MB was taken from applet over 4.x and given to system. */ - {0x01E100000ULL, 0x080000000ULL, 0x061800000ULL}, /* No changes. */ - {0x015E00000ULL, 0x0CD500000ULL, 0x01C600000ULL}, /* 12 MB was taken from applet over 4.x and given to system. */ - {0x028D00000ULL, 0x133400000ULL, 0x023800000ULL}, /* No changes. */ - {0x028D00000ULL, 0x0CD500000ULL, 0x089700000ULL} /* No changes. */ -}; - -/* These are the limits for LimitableResources. */ -/* Memory, Threads, Events, TransferMemories, Sessions. */ -static u64 g_resource_limits_deprecated[3][5] = { - {0x0, 0x1FC, 0x258, 0x80, 0x31A}, - {0x0, 0x60, 0x0, 0x20, 0x1}, - {0x0, 0x60, 0x0, 0x20, 0x5}, -}; - -/* 4.x boosted the number of threads that system modules are allowed to make. */ -static u64 g_resource_limits_4x[3][5] = { - {0x0, 0x260, 0x258, 0x80, 0x31A}, - {0x0, 0x60, 0x0, 0x20, 0x1}, - {0x0, 0x60, 0x0, 0x20, 0x5}, -}; - -/* 6.x boosted the number of events and sessions that system modules are allowed to make. */ -static u64 g_resource_limits_6x[3][5] = { - {0x0, 0x260, 0x2BC, 0x80, 0x37E}, - {0x0, 0x60, 0x0, 0x20, 0x1}, - {0x0, 0x60, 0x0, 0x20, 0x5}, -}; - -static Handle g_resource_limit_handles[3] = {0}; - - -static u64 g_memory_resource_limits[6][3] = {0}; -static u64 g_resource_limits[3][5] = {0}; -static int g_memory_limit_type; -static u64 g_system_boost_size = 0; - -/* Tries to set Resource limits for a category. */ -static Result SetResourceLimits(ResourceLimitUtils::ResourceLimitCategory category, u64 new_memory_size) { - u64 old_memory_size = g_resource_limits[category][LimitableResource_Memory]; - g_resource_limits[category][LimitableResource_Memory] = new_memory_size; - for (unsigned int r = 0; r < 5; r++) { - R_TRY_CLEANUP(svcSetResourceLimitLimitValue(g_resource_limit_handles[category], (LimitableResource)r, g_resource_limits[category][r]), { - g_resource_limits[category][LimitableResource_Memory] = old_memory_size; - }); - } - return ResultSuccess; -} - - -static Result SetNewMemoryResourceLimit(ResourceLimitUtils::ResourceLimitCategory category, u64 new_memory_size) { - u64 old_memory_size = g_resource_limits[category][LimitableResource_Memory]; - g_resource_limits[category][LimitableResource_Memory] = new_memory_size; - R_TRY_CLEANUP(svcSetResourceLimitLimitValue(g_resource_limit_handles[category], LimitableResource_Memory, g_resource_limits[category][LimitableResource_Memory]), { - g_resource_limits[category][LimitableResource_Memory] = old_memory_size; - }); - return ResultSuccess; -} - -void ResourceLimitUtils::InitializeLimits() { - /* Create Resource Limits. */ - for (unsigned int i = 0; i < 3; i++) { - if (i > 0) { - R_ASSERT(svcCreateResourceLimit(&g_resource_limit_handles[i])); - } else { - u64 out = 0; - R_ASSERT(svcGetInfo(&out, 9, 0, 0)); - g_resource_limit_handles[i] = (Handle)out; - } - } - /* Set global aliases. */ - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_600) { - /* 6.0.0 did away with hardcoded memory resource limit values. */ - memcpy(&g_resource_limits, &g_resource_limits_6x, sizeof(g_resource_limits)); - } else if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { - memcpy(&g_memory_resource_limits, &g_memory_resource_limits_5x, sizeof(g_memory_resource_limits_5x)); - memcpy(&g_resource_limits, &g_resource_limits_4x, sizeof(g_resource_limits)); - } else if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) { - memcpy(&g_memory_resource_limits, &g_memory_resource_limits_4x, sizeof(g_memory_resource_limits_4x)); - memcpy(&g_resource_limits, &g_resource_limits_4x, sizeof(g_resource_limits)); - } else { - memcpy(&g_memory_resource_limits, &g_memory_resource_limits_deprecated, sizeof(g_memory_resource_limits_deprecated)); - memcpy(&g_resource_limits, &g_resource_limits_deprecated, sizeof(g_resource_limits)); - } - - /* 7.0.0+: Nintendo restricts the number of system threads here, from 0x260 -> 0x60. */ - /* We will not do this. */ - - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_600) { - /* NOTE: 5 is a fake type, official code does not do this. */ - /* This is done for ease of backwards compatibility. */ - g_memory_limit_type = 5; - - /* Starting 6.x, 5 MB of memory is always reserved for system. */ - const u64 reserved_system_size_6x = 0x500000; - - /* Get total memory available. */ - u64 total_memory = 0; - R_ASSERT(svcGetResourceLimitLimitValue(&total_memory, g_resource_limit_handles[0], LimitableResource_Memory)); - - /* Get and save application + applet memory. */ - R_ASSERT(svcGetSystemInfo(&g_memory_resource_limits[g_memory_limit_type][1], 0, 0, 0)); - R_ASSERT(svcGetSystemInfo(&g_memory_resource_limits[g_memory_limit_type][2], 0, 0, 1)); - - const u64 application_size = g_memory_resource_limits[g_memory_limit_type][1]; - const u64 applet_size = g_memory_resource_limits[g_memory_limit_type][2]; - const u64 reserved_nonsys_size = (application_size + applet_size + reserved_system_size_6x); - - /* Ensure there's enough memory for system region. */ - if (reserved_nonsys_size > total_memory) { - std::abort(); - } - - /* Set System memory. */ - g_memory_resource_limits[g_memory_limit_type][0] = total_memory - (reserved_nonsys_size); - } else { - /* Get memory limits. */ - u64 memory_arrangement; - R_ASSERT(splGetConfig(SplConfigItem_MemoryArrange, &memory_arrangement)); - memory_arrangement &= 0x3F; - switch (memory_arrangement) { - case 2: - g_memory_limit_type = 1; - break; - case 3: - g_memory_limit_type = 2; - break; - case 17: - g_memory_limit_type = 3; - break; - case 18: - g_memory_limit_type = 4; - break; - default: - g_memory_limit_type = 0; - break; - } - } - - /* Atmosphere: Allocate extra memory (24 MiB) to SYSTEM away from Applet. */ - for (unsigned int i = 0; i < 6; i++) { - g_memory_resource_limits[i][0] += ATMOSPHERE_EXTRA_SYSTEM_MEMORY_FOR_SYSMODULES; - /* On < 3.0.0, taking from application instead of applet fixes a rare hang on boot. */ - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) { - g_memory_resource_limits[i][2] -= ATMOSPHERE_EXTRA_SYSTEM_MEMORY_FOR_SYSMODULES; - } else { - g_memory_resource_limits[i][1] -= ATMOSPHERE_EXTRA_SYSTEM_MEMORY_FOR_SYSMODULES; - } - } - - /* Set resource limits. */ - for (unsigned int i = 0; i < 3; i++) { - g_resource_limits[i][LimitableResource_Memory] = g_memory_resource_limits[g_memory_limit_type][i]; - R_ASSERT(SetResourceLimits((ResourceLimitCategory)i, g_memory_resource_limits[g_memory_limit_type][i])); - } -} - -void ResourceLimitUtils::EnsureApplicationResourcesAvailable() { - Handle application_reslimit_h = g_resource_limit_handles[1]; - for (unsigned int i = 0; i < 5; i++) { - u64 result; - do { - if (R_FAILED(svcGetResourceLimitCurrentValue(&result, application_reslimit_h, (LimitableResource)i))) { - return; - } - svcSleepThread(1000000ULL); - } while (result); - } - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { - u64 result; - do { - R_ASSERT(svcGetSystemInfo(&result, 1, 0, 0)); - svcSleepThread(1000000ULL); - } while (result); - } -} - -Handle ResourceLimitUtils::GetResourceLimitHandle(u16 application_type) { - if ((application_type & 3) == 1) { - return g_resource_limit_handles[1]; - } else { - return g_resource_limit_handles[2 * ((application_type & 3) == 2)]; - } -} - -Handle ResourceLimitUtils::GetResourceLimitHandleByCategory(ResourceLimitCategory category) { - return g_resource_limit_handles[category]; -} - -Result ResourceLimitUtils::BoostSystemMemoryResourceLimit(u64 boost_size) { - if (boost_size > g_memory_resource_limits[g_memory_limit_type][ResourceLimitCategory_Application]) { - return ResultPmInvalidSize; - } - - u64 app_size = g_memory_resource_limits[g_memory_limit_type][ResourceLimitCategory_Application] - boost_size; - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { - if (boost_size < g_system_boost_size) { - R_TRY(svcSetUnsafeLimit(boost_size)); - R_TRY(SetNewMemoryResourceLimit(ResourceLimitCategory_Application, app_size)); - } else { - R_TRY(SetNewMemoryResourceLimit(ResourceLimitCategory_Application, app_size)); - R_TRY(svcSetUnsafeLimit(boost_size)); - } - } else if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) { - u64 sys_size = g_memory_resource_limits[g_memory_limit_type][ResourceLimitCategory_System] + boost_size; - if (boost_size < g_system_boost_size) { - R_TRY(SetResourceLimits(ResourceLimitCategory_System, sys_size)); - R_TRY(SetResourceLimits(ResourceLimitCategory_Application, app_size)); - } else { - R_TRY(SetResourceLimits(ResourceLimitCategory_Application, app_size)); - R_TRY(SetResourceLimits(ResourceLimitCategory_System, sys_size)); - } - } else { - return ResultKernelConnectionClosed; - } - - g_system_boost_size = boost_size; - return ResultSuccess; -} diff --git a/stratosphere/pm/source/pm_shell.cpp b/stratosphere/pm/source/pm_shell.cpp deleted file mode 100644 index ab1af5b03..000000000 --- a/stratosphere/pm/source/pm_shell.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include "pm_registration.hpp" -#include "pm_resource_limits.hpp" -#include "pm_shell.hpp" -#include "pm_boot2.hpp" - -static bool g_has_boot_finished = false; - -Result ShellService::LaunchProcess(Out pid, Registration::TidSid tid_sid, u32 launch_flags) { - return Registration::LaunchProcessByTidSid(tid_sid, launch_flags, pid.GetPointer()); -} - -Result ShellService::TerminateProcessId(u64 pid) { - std::scoped_lock lk(Registration::GetProcessList()); - - auto proc = Registration::GetProcess(pid); - if (proc != nullptr) { - return svcTerminateProcess(proc->handle); - } else { - return ResultPmProcessNotFound; - } -} - -Result ShellService::TerminateTitleId(u64 tid) { - std::scoped_lock lk(Registration::GetProcessList()); - - auto proc = Registration::GetProcessByTitleId(tid); - if (proc != NULL) { - return svcTerminateProcess(proc->handle); - } else { - return ResultPmProcessNotFound; - } -} - -void ShellService::GetProcessWaitEvent(Out event) { - event.SetValue(Registration::GetProcessEventHandle()); -} - -void ShellService::GetProcessEventType(Out type, Out pid) { - Registration::GetProcessEventType(pid.GetPointer(), type.GetPointer()); -} - -Result ShellService::FinalizeExitedProcess(u64 pid) { - std::scoped_lock lk(Registration::GetProcessList()); - - auto proc = Registration::GetProcess(pid); - if (proc == NULL) { - return ResultPmProcessNotFound; - } else if (proc->state != ProcessState_Exited) { - return ResultPmNotExited; - } else { - Registration::FinalizeExitedProcess(proc); - return ResultSuccess; - } -} - -Result ShellService::ClearProcessNotificationFlag(u64 pid) { - std::scoped_lock lk(Registration::GetProcessList()); - - auto proc = Registration::GetProcess(pid); - if (proc != NULL) { - proc->flags &= ~PROCESSFLAGS_CRASHED; - return ResultSuccess; - } else { - return ResultPmProcessNotFound; - } -} - -void ShellService::NotifyBootFinished() { - if (!g_has_boot_finished) { - g_has_boot_finished = true; - Registration::SignalBootFinished(); - EmbeddedBoot2::Main(); - } -} - -Result ShellService::GetApplicationProcessId(Out pid) { - std::scoped_lock lk(Registration::GetProcessList()); - - std::shared_ptr app_proc; - if (Registration::HasApplicationProcess(&app_proc)) { - pid.SetValue(app_proc->pid); - return ResultSuccess; - } - return ResultPmProcessNotFound; -} - -Result ShellService::BoostSystemMemoryResourceLimit(u64 sysmem_size) { - return ResourceLimitUtils::BoostSystemMemoryResourceLimit(sysmem_size); -} - -Result ShellService::BoostSystemThreadsResourceLimit() { - /* Starting in 7.0.0, Nintendo reduces the number of system threads from 0x260 to 0x60, */ - /* Until this command is called to double that amount to 0xC0. */ - /* We will simply not reduce the number of system threads available for no reason. */ - return ResultSuccess; -} - - -void ShellService::GetBootFinishedEvent(Out event) { - /* 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. */ - { - u64 is_recovery_boot = 0; - R_ASSERT(SmcGetConfig(SplConfigItem_IsRecoveryBoot, &is_recovery_boot)); - if (!is_recovery_boot) { - std::abort(); - } - } - event.SetValue(Registration::GetBootFinishedEventHandle()); -} diff --git a/stratosphere/pm/source/pm_shell.hpp b/stratosphere/pm/source/pm_shell.hpp deleted file mode 100644 index f2bc7bc42..000000000 --- a/stratosphere/pm/source/pm_shell.hpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once -#include -#include - -#include "pm_registration.hpp" - -/* Represents modern ShellService (5.0.0+) */ -class ShellService : public IServiceObject { - private: - enum class CommandId { - LaunchProcess = 0, - TerminateProcessId = 1, - TerminateTitleId = 2, - GetProcessWaitEvent = 3, - GetProcessEventType = 4, - NotifyBootFinished = 5, - GetApplicationProcessId = 6, - BoostSystemMemoryResourceLimit = 7, - BoostSystemThreadsResourceLimit = 8, - GetBootFinishedEvent = 9, - }; - protected: - /* Actual commands. */ - Result LaunchProcess(Out pid, Registration::TidSid tid_sid, u32 launch_flags); - Result TerminateProcessId(u64 pid); - Result TerminateTitleId(u64 tid); - void GetProcessWaitEvent(Out event); - void GetProcessEventType(Out type, Out pid); - Result FinalizeExitedProcess(u64 pid); - Result ClearProcessNotificationFlag(u64 pid); - void NotifyBootFinished(); - Result GetApplicationProcessId(Out pid); - Result BoostSystemMemoryResourceLimit(u64 sysmem_size); - Result BoostSystemThreadsResourceLimit(); - void GetBootFinishedEvent(Out event); - public: - DEFINE_SERVICE_DISPATCH_TABLE { - /* 5.0.0-* */ - MAKE_SERVICE_COMMAND_META(ShellService, LaunchProcess), - MAKE_SERVICE_COMMAND_META(ShellService, TerminateProcessId), - MAKE_SERVICE_COMMAND_META(ShellService, TerminateTitleId), - MAKE_SERVICE_COMMAND_META(ShellService, GetProcessWaitEvent), - MAKE_SERVICE_COMMAND_META(ShellService, GetProcessEventType), - MAKE_SERVICE_COMMAND_META(ShellService, NotifyBootFinished), - MAKE_SERVICE_COMMAND_META(ShellService, GetApplicationProcessId), - MAKE_SERVICE_COMMAND_META(ShellService, BoostSystemMemoryResourceLimit), - - /* 7.0.0-* */ - MAKE_SERVICE_COMMAND_META(ShellService, BoostSystemThreadsResourceLimit, FirmwareVersion_700), - - /* 8.0.0-* */ - MAKE_SERVICE_COMMAND_META(ShellService, GetBootFinishedEvent, FirmwareVersion_800), - }; -}; - -/* Represents deprecated ShellService (1.0.0-4.1.0). */ -class ShellServiceDeprecated : public ShellService { - private: - enum class CommandId { - LaunchProcess = 0, - TerminateProcessId = 1, - TerminateTitleId = 2, - GetProcessWaitEvent = 3, - GetProcessEventType = 4, - FinalizeExitedProcess = 5, - ClearProcessNotificationFlag = 6, - NotifyBootFinished = 7, - GetApplicationProcessId = 8, - BoostSystemMemoryResourceLimit = 9, - }; - public: - DEFINE_SERVICE_DISPATCH_TABLE { - /* 1.0.0- */ - MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, LaunchProcess), - MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, TerminateProcessId), - MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, TerminateTitleId), - MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, GetProcessWaitEvent), - MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, GetProcessEventType), - MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, FinalizeExitedProcess), - MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, ClearProcessNotificationFlag), - MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, NotifyBootFinished), - MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, GetApplicationProcessId), - - /* 4.0.0- */ - MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, BoostSystemMemoryResourceLimit, FirmwareVersion_400), - }; -}; diff --git a/stratosphere/pm/source/pm_shell_service.cpp b/stratosphere/pm/source/pm_shell_service.cpp new file mode 100644 index 000000000..c82803f4e --- /dev/null +++ b/stratosphere/pm/source/pm_shell_service.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "pm_shell_service.hpp" +#include "impl/pm_process_manager.hpp" + +namespace sts::pm::shell { + + /* Overrides for libstratosphere pm::shell commands. */ + Result LaunchTitle(u64 *out_process_id, const ncm::TitleLocation &loc, u32 launch_flags) { + return impl::LaunchTitle(out_process_id, loc, launch_flags); + } + + /* Service command implementations. */ + Result ShellServiceBase::LaunchTitle(Out out_process_id, ncm::TitleLocation loc, u32 flags) { + return pm::shell::LaunchTitle(out_process_id.GetPointer(), loc, flags); + } + + Result ShellServiceBase::TerminateProcess(u64 process_id) { + return impl::TerminateProcess(process_id); + } + + Result ShellServiceBase::TerminateTitle(ncm::TitleId title_id) { + return impl::TerminateTitle(title_id); + } + + void ShellServiceBase::GetProcessEventHandle(Out out) { + R_ASSERT(impl::GetProcessEventHandle(out.GetHandlePointer())); + } + + void ShellServiceBase::GetProcessEventInfo(Out out) { + R_ASSERT(impl::GetProcessEventInfo(out.GetPointer())); + } + + Result ShellServiceBase::CleanupProcess(u64 process_id) { + return impl::CleanupProcess(process_id); + } + + Result ShellServiceBase::ClearExceptionOccurred(u64 process_id) { + return impl::ClearExceptionOccurred(process_id); + } + + void ShellServiceBase::NotifyBootFinished() { + R_ASSERT(impl::NotifyBootFinished()); + } + + Result ShellServiceBase::GetApplicationProcessIdForShell(Out out) { + return impl::GetApplicationProcessId(out.GetPointer()); + } + + Result ShellServiceBase::BoostSystemMemoryResourceLimit(u64 boost_size) { + return impl::BoostSystemMemoryResourceLimit(boost_size); + } + + Result ShellServiceBase::BoostSystemThreadResourceLimit() { + return impl::BoostSystemThreadResourceLimit(); + } + + void ShellServiceBase::GetBootFinishedEventHandle(Out out) { + R_ASSERT(impl::GetBootFinishedEventHandle(out.GetHandlePointer())); + } + +} diff --git a/stratosphere/pm/source/pm_shell_service.hpp b/stratosphere/pm/source/pm_shell_service.hpp new file mode 100644 index 000000000..101293044 --- /dev/null +++ b/stratosphere/pm/source/pm_shell_service.hpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include + +namespace sts::pm::shell { + + class ShellServiceBase : public IServiceObject { + protected: + /* Actual command implementations. */ + virtual Result LaunchTitle(Out out_process_id, ncm::TitleLocation loc, u32 flags); + virtual Result TerminateProcess(u64 process_id); + virtual Result TerminateTitle(ncm::TitleId title_id); + virtual void GetProcessEventHandle(Out out); + virtual void GetProcessEventInfo(Out out); + virtual Result CleanupProcess(u64 process_id); + virtual Result ClearExceptionOccurred(u64 process_id); + virtual void NotifyBootFinished(); + virtual Result GetApplicationProcessIdForShell(Out out); + virtual Result BoostSystemMemoryResourceLimit(u64 boost_size); + virtual Result BoostSystemThreadResourceLimit(); + virtual void GetBootFinishedEventHandle(Out out); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* No entries, because ShellServiceBase is abstract. */ + }; + }; + + /* This represents modern ShellService (5.0.0+). */ + class ShellService final : public ShellServiceBase { + private: + enum class CommandId { + LaunchTitle = 0, + TerminateProcess = 1, + TerminateTitle = 2, + GetProcessEventHandle = 3, + GetProcessEventInfo = 4, + NotifyBootFinished = 5, + GetApplicationProcessIdForShell = 6, + BoostSystemMemoryResourceLimit = 7, + BoostSystemThreadResourceLimit = 8, + GetBootFinishedEventHandle = 9, + }; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* 5.0.0-* */ + MAKE_SERVICE_COMMAND_META(ShellService, LaunchTitle), + MAKE_SERVICE_COMMAND_META(ShellService, TerminateProcess), + MAKE_SERVICE_COMMAND_META(ShellService, TerminateTitle), + MAKE_SERVICE_COMMAND_META(ShellService, GetProcessEventHandle), + MAKE_SERVICE_COMMAND_META(ShellService, GetProcessEventInfo), + MAKE_SERVICE_COMMAND_META(ShellService, NotifyBootFinished), + MAKE_SERVICE_COMMAND_META(ShellService, GetApplicationProcessIdForShell), + MAKE_SERVICE_COMMAND_META(ShellService, BoostSystemMemoryResourceLimit), + + /* 7.0.0-* */ + MAKE_SERVICE_COMMAND_META(ShellService, BoostSystemThreadResourceLimit, FirmwareVersion_700), + + /* 8.0.0-* */ + MAKE_SERVICE_COMMAND_META(ShellService, GetBootFinishedEventHandle, FirmwareVersion_800), + }; + }; + + /* This represents deprecated ShellService (1.0.0-4.1.0). */ + class ShellServiceDeprecated final : public ShellServiceBase { + private: + enum class CommandId { + LaunchTitle = 0, + TerminateProcess = 1, + TerminateTitle = 2, + GetProcessEventHandle = 3, + GetProcessEventInfo = 4, + CleanupProcess = 5, + ClearExceptionOccurred = 6, + NotifyBootFinished = 7, + GetApplicationProcessIdForShell = 8, + BoostSystemMemoryResourceLimit = 9, + }; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* 1.0.0-4.1.0 */ + MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, LaunchTitle), + MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, TerminateProcess), + MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, TerminateTitle), + MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, GetProcessEventHandle), + MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, GetProcessEventInfo), + MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, CleanupProcess), + MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, ClearExceptionOccurred), + MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, NotifyBootFinished), + MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, GetApplicationProcessIdForShell), + + /* 4.0.0-4.1.0 */ + MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, BoostSystemMemoryResourceLimit), + }; + }; + +}