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