mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
pm: complete rewrite
This commit is contained in:
parent
5bba0f47ff
commit
20a48c3a26
32 changed files with 2309 additions and 1998 deletions
|
@ -1 +1 @@
|
|||
Subproject commit 3f6df380c8becb4ba0a013cf0533364163a741d8
|
||||
Subproject commit d6eacecce97d93b8261a9def8f69385f6a231dc9
|
|
@ -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
|
||||
|
|
294
stratosphere/pm/source/boot2/boot2_api.cpp
Normal file
294
stratosphere/pm/source/boot2/boot2_api.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cctype>
|
||||
#include <dirent.h>
|
||||
#include <stratosphere/cfg.hpp>
|
||||
#include <stratosphere/ldr.hpp>
|
||||
#include <stratosphere/pm.hpp>
|
||||
#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<unsigned char>(*(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();
|
||||
}
|
||||
|
||||
}
|
|
@ -16,8 +16,11 @@
|
|||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
class ProcessTracking {
|
||||
public:
|
||||
static void MainLoop(void *arg);
|
||||
};
|
||||
namespace sts::boot2 {
|
||||
|
||||
/* Boot2 API. */
|
||||
void LaunchBootPrograms();
|
||||
|
||||
}
|
45
stratosphere/pm/source/impl/pm_process_info.cpp
Normal file
45
stratosphere/pm/source/impl/pm_process_info.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stratosphere/sm/sm_manager_api.hpp>
|
||||
#include <stratosphere/ldr/ldr_pm_api.hpp>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
310
stratosphere/pm/source/impl/pm_process_info.hpp
Normal file
310
stratosphere/pm/source/impl/pm_process_info.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/ldr.hpp>
|
||||
#include <stratosphere/pm.hpp>
|
||||
|
||||
#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<ProcessInfo> process_info);
|
||||
|
||||
class ProcessInfoWaiter final : public IWaitable {
|
||||
private:
|
||||
std::shared_ptr<ProcessInfo> process_info;
|
||||
public:
|
||||
ProcessInfoWaiter(std::shared_ptr<ProcessInfo> 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<std::shared_ptr<ProcessInfo>> processes;
|
||||
public:
|
||||
void Lock() {
|
||||
this->lock.Lock();
|
||||
}
|
||||
|
||||
void Unlock() {
|
||||
this->lock.Unlock();
|
||||
}
|
||||
|
||||
size_t GetSize() const {
|
||||
return this->processes.size();
|
||||
}
|
||||
|
||||
std::shared_ptr<ProcessInfo> Pop() {
|
||||
auto front = this->processes[0];
|
||||
this->processes.erase(this->processes.begin());
|
||||
return front;
|
||||
}
|
||||
|
||||
void Add(std::shared_ptr<ProcessInfo> 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<ProcessInfo> 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<ProcessInfo> 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<ProcessInfo> operator[](int i) {
|
||||
return this->processes[i];
|
||||
}
|
||||
|
||||
const std::shared_ptr<ProcessInfo> 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<ProcessInfo> operator[](int i) {
|
||||
return this->list[i];
|
||||
}
|
||||
|
||||
const std::shared_ptr<ProcessInfo> operator[](int i) const {
|
||||
return this->list[i];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
679
stratosphere/pm/source/impl/pm_process_manager.cpp
Normal file
679
stratosphere/pm/source/impl/pm_process_manager.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <stratosphere/spl.hpp>
|
||||
#include <stratosphere/ldr/ldr_pm_api.hpp>
|
||||
#include <stratosphere/sm/sm_manager_api.hpp>
|
||||
|
||||
#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<u32>(LaunchFlags_##flag) : static_cast<u32>(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<u32>(event);
|
||||
}
|
||||
switch (event) {
|
||||
case ProcessEvent::None:
|
||||
return static_cast<u32>(ProcessEventDeprecated::None);
|
||||
case ProcessEvent::Exited:
|
||||
return static_cast<u32>(ProcessEventDeprecated::Exited);
|
||||
case ProcessEvent::Started:
|
||||
return static_cast<u32>(ProcessEventDeprecated::Started);
|
||||
case ProcessEvent::Exception:
|
||||
return static_cast<u32>(ProcessEventDeprecated::Exception);
|
||||
case ProcessEvent::DebugRunning:
|
||||
return static_cast<u32>(ProcessEventDeprecated::DebugRunning);
|
||||
case ProcessEvent::DebugSuspended:
|
||||
return static_cast<u32>(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<ncm::TitleId> g_title_id_hook;
|
||||
std::atomic<bool> 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<ProcessInfo> 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<ncm::StorageId>(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<ProcessInfo>(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<u64>(location.title_id), static_cast<FsStorageId>(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<ProcessInfo> 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<ProcessInfo> 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<ProcessState>(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<HosMutex> 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<u32>(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<ResourceLimitGroup>(group), static_cast<LimitableResource>(resource));
|
||||
}
|
||||
|
||||
}
|
60
stratosphere/pm/source/impl/pm_process_manager.hpp
Normal file
60
stratosphere/pm/source/impl/pm_process_manager.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/ldr.hpp>
|
||||
#include <stratosphere/pm.hpp>
|
||||
|
||||
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);
|
||||
|
||||
}
|
334
stratosphere/pm/source/impl/pm_resource_manager.cpp
Normal file
334
stratosphere/pm/source/impl/pm_resource_manager.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stratosphere/spl.hpp>
|
||||
|
||||
#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<Handle>(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<HosMutex> lk(g_resource_limit_lock);
|
||||
|
||||
for (size_t group = 0; group < ResourceLimitGroup_Count; group++) {
|
||||
R_ASSERT(SetResourceLimitLimitValues(static_cast<ResourceLimitGroup>(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<HosMutex> 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -17,17 +17,19 @@
|
|||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/ldr.hpp>
|
||||
#include <stratosphere/pm.hpp>
|
||||
|
||||
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);
|
||||
};
|
||||
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);
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <dirent.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/sm/sm_manager_api.hpp>
|
||||
|
||||
#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<u64, bool> 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<bool>(launch_program)) {
|
||||
LaunchTitle(std::get<u64>(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<u64>(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();
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class EmbeddedBoot2 {
|
||||
public:
|
||||
static void Main();
|
||||
};
|
|
@ -14,20 +14,33 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#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<u32> out) {
|
||||
out.SetValue(static_cast<u32>(pm::bm::GetBootMode()));
|
||||
}
|
||||
|
||||
void BootModeService::SetMaintenanceBoot() {
|
||||
pm::bm::SetMaintenanceBoot();
|
||||
}
|
||||
|
||||
void BootModeService::GetBootMode(Out<u32> out) {
|
||||
out.SetValue(g_is_maintenance_boot);
|
||||
}
|
||||
|
||||
void BootModeService::SetMaintenanceBoot() {
|
||||
g_is_maintenance_boot = true;
|
||||
}
|
||||
|
||||
void BootModeService::SetMaintenanceBootForEmbeddedBoot2() {
|
||||
g_is_maintenance_boot = true;
|
||||
}
|
|
@ -17,22 +17,25 @@
|
|||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/pm.hpp>
|
||||
|
||||
class BootModeService final : public IServiceObject {
|
||||
private:
|
||||
enum class CommandId {
|
||||
GetBootMode = 0,
|
||||
SetMaintenanceBoot = 1,
|
||||
};
|
||||
private:
|
||||
/* Actual commands. */
|
||||
void GetBootMode(Out<u32> 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<u32> out);
|
||||
void SetMaintenanceBoot();
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
MAKE_SERVICE_COMMAND_META(BootModeService, GetBootMode),
|
||||
MAKE_SERVICE_COMMAND_META(BootModeService, SetMaintenanceBoot),
|
||||
};
|
||||
};
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "pm_registration.hpp"
|
||||
#include "pm_resource_limits.hpp"
|
||||
#include "pm_debug_monitor.hpp"
|
||||
|
||||
|
||||
Result DebugMonitorService::GetUnknownStub(Out<u32> count, OutBuffer<u8> 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<u32> count, OutBuffer<u64> 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<u64> pid, u64 tid) {
|
||||
std::scoped_lock<ProcessList &> lk(Registration::GetProcessList());
|
||||
|
||||
std::shared_ptr<Registration::Process> proc = Registration::GetProcessByTitleId(tid);
|
||||
if (proc != nullptr) {
|
||||
pid.SetValue(proc->pid);
|
||||
return ResultSuccess;
|
||||
}
|
||||
return ResultPmProcessNotFound;
|
||||
}
|
||||
|
||||
Result DebugMonitorService::EnableDebugForTitleId(Out<CopiedHandle> event, u64 tid) {
|
||||
return Registration::EnableDebugForTitleId(tid, event.GetHandlePointer());
|
||||
}
|
||||
|
||||
Result DebugMonitorService::GetApplicationProcessId(Out<u64> pid) {
|
||||
std::scoped_lock<ProcessList &> lk(Registration::GetProcessList());
|
||||
|
||||
std::shared_ptr<Registration::Process> app_proc;
|
||||
if (Registration::HasApplicationProcess(&app_proc)) {
|
||||
pid.SetValue(app_proc->pid);
|
||||
return ResultSuccess;
|
||||
}
|
||||
return ResultPmProcessNotFound;
|
||||
}
|
||||
|
||||
Result DebugMonitorService::EnableDebugForApplication(Out<CopiedHandle> event) {
|
||||
return Registration::EnableDebugForApplication(event.GetHandlePointer());
|
||||
}
|
||||
|
||||
|
||||
Result DebugMonitorService::DisableDebug(u32 which) {
|
||||
return Registration::DisableDebug(which);
|
||||
}
|
||||
|
||||
Result DebugMonitorService::AtmosphereGetProcessInfo(Out<CopiedHandle> proc_hand, Out<Registration::TidSid> 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<u64> cur_val, Out<u64> 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;
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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<u32> count, OutBuffer<u8> out_buf, u64 in_unk);
|
||||
Result GetDebugProcessIds(Out<u32> count, OutBuffer<u64> out_pids);
|
||||
Result LaunchDebugProcess(u64 pid);
|
||||
Result GetTitleProcessId(Out<u64> pid, u64 tid);
|
||||
Result EnableDebugForTitleId(Out<CopiedHandle> event, u64 tid);
|
||||
Result GetApplicationProcessId(Out<u64> pid);
|
||||
Result EnableDebugForApplication(Out<CopiedHandle> event);
|
||||
Result DisableDebug(u32 which);
|
||||
|
||||
/* Atmosphere commands. */
|
||||
Result AtmosphereGetProcessInfo(Out<CopiedHandle> proc_hand, Out<Registration::TidSid> tid_sid, u64 pid);
|
||||
Result AtmosphereGetCurrentLimitInfo(Out<u64> cur_val, Out<u64> 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),
|
||||
};
|
||||
};
|
71
stratosphere/pm/source/pm_debug_monitor_service.cpp
Normal file
71
stratosphere/pm/source/pm_debug_monitor_service.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <limits>
|
||||
#include "pm_debug_monitor_service.hpp"
|
||||
#include "impl/pm_process_manager.hpp"
|
||||
|
||||
namespace sts::pm::dmnt {
|
||||
|
||||
/* Actual command implementations. */
|
||||
Result DebugMonitorServiceBase::GetModuleIdList(Out<u32> out_count, OutBuffer<u8> out_buf, u64 unused) {
|
||||
if (out_buf.num_elements > std::numeric_limits<s32>::max()) {
|
||||
return ResultPmInvalidSize;
|
||||
}
|
||||
return impl::GetModuleIdList(out_count.GetPointer(), out_buf.buffer, out_buf.num_elements, unused);
|
||||
}
|
||||
|
||||
Result DebugMonitorServiceBase::GetExceptionProcessIdList(Out<u32> out_count, OutBuffer<u64> out_process_ids) {
|
||||
if (out_process_ids.num_elements > std::numeric_limits<s32>::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<u64> out, ncm::TitleId title_id) {
|
||||
return impl::GetProcessId(out.GetPointer(), title_id);
|
||||
}
|
||||
|
||||
Result DebugMonitorServiceBase::HookToCreateProcess(Out<CopiedHandle> out_hook, ncm::TitleId title_id) {
|
||||
return impl::HookToCreateProcess(out_hook.GetHandlePointer(), title_id);
|
||||
}
|
||||
|
||||
Result DebugMonitorServiceBase::GetApplicationProcessId(Out<u64> out) {
|
||||
return impl::GetApplicationProcessId(out.GetPointer());
|
||||
}
|
||||
|
||||
Result DebugMonitorServiceBase::HookToCreateApplicationProcess(Out<CopiedHandle> out_hook) {
|
||||
return impl::HookToCreateApplicationProcess(out_hook.GetHandlePointer());
|
||||
}
|
||||
|
||||
Result DebugMonitorServiceBase::ClearHook(u32 which) {
|
||||
return impl::ClearHook(which);
|
||||
}
|
||||
|
||||
/* Atmosphere extension commands. */
|
||||
Result DebugMonitorServiceBase::AtmosphereGetProcessInfo(Out<CopiedHandle> out_process_handle, Out<ncm::TitleLocation> out_loc, u64 process_id) {
|
||||
return impl::AtmosphereGetProcessInfo(out_process_handle.GetHandlePointer(), out_loc.GetPointer(), process_id);
|
||||
}
|
||||
|
||||
Result DebugMonitorServiceBase::AtmosphereGetCurrentLimitInfo(Out<u64> out_cur_val, Out<u64> out_lim_val, u32 group, u32 resource) {
|
||||
return impl::AtmosphereGetCurrentLimitInfo(out_cur_val.GetPointer(), out_lim_val.GetPointer(), group, resource);
|
||||
}
|
||||
|
||||
}
|
113
stratosphere/pm/source/pm_debug_monitor_service.hpp
Normal file
113
stratosphere/pm/source/pm_debug_monitor_service.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/ldr.hpp>
|
||||
#include <stratosphere/pm.hpp>
|
||||
|
||||
namespace sts::pm::dmnt {
|
||||
|
||||
class DebugMonitorServiceBase : public IServiceObject {
|
||||
protected:
|
||||
/* Actual command implementations. */
|
||||
virtual Result GetModuleIdList(Out<u32> out_count, OutBuffer<u8> out_buf, u64 unused);
|
||||
virtual Result GetExceptionProcessIdList(Out<u32> out_count, OutBuffer<u64> out_process_ids);
|
||||
virtual Result StartProcess(u64 process_id);
|
||||
virtual Result GetProcessId(Out<u64> out, ncm::TitleId title_id);
|
||||
virtual Result HookToCreateProcess(Out<CopiedHandle> out_hook, ncm::TitleId title_id);
|
||||
virtual Result GetApplicationProcessId(Out<u64> out);
|
||||
virtual Result HookToCreateApplicationProcess(Out<CopiedHandle> out_hook);
|
||||
virtual Result ClearHook(u32 which);
|
||||
|
||||
/* Atmosphere extension commands. */
|
||||
virtual Result AtmosphereGetProcessInfo(Out<CopiedHandle> out_process_handle, Out<ncm::TitleLocation> out_loc, u64 process_id);
|
||||
virtual Result AtmosphereGetCurrentLimitInfo(Out<u64> out_cur_val, Out<u64> 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),
|
||||
};
|
||||
};
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere/ldr.hpp>
|
||||
#include <stratosphere/ldr/ldr_pm_api.hpp>
|
||||
|
||||
#include "pm_registration.hpp"
|
||||
#include "pm_info.hpp"
|
||||
|
||||
Result InformationService::GetTitleId(Out<u64> tid, u64 pid) {
|
||||
std::scoped_lock<ProcessList &> lk(Registration::GetProcessList());
|
||||
|
||||
std::shared_ptr<Registration::Process> proc = Registration::GetProcess(pid);
|
||||
if (proc == NULL) {
|
||||
return ResultPmProcessNotFound;
|
||||
}
|
||||
|
||||
tid.SetValue(proc->tid_sid.title_id);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InformationService::AtmosphereGetProcessId(Out<u64> pid, u64 tid) {
|
||||
std::scoped_lock<ProcessList &> lk(Registration::GetProcessList());
|
||||
|
||||
std::shared_ptr<Registration::Process> proc = Registration::GetProcessByTitleId(tid);
|
||||
if (proc == nullptr) {
|
||||
return ResultPmProcessNotFound;
|
||||
}
|
||||
|
||||
pid.SetValue(proc->pid);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InformationService::AtmosphereHasLaunchedTitle(Out<bool> out, u64 tid) {
|
||||
return sts::ldr::pm::HasLaunchedTitle(out.GetPointer(), sts::ncm::TitleId{tid});
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
class InformationService final : public IServiceObject {
|
||||
private:
|
||||
enum class CommandId {
|
||||
GetTitleId = 0,
|
||||
AtmosphereGetProcessId = 65000,
|
||||
AtmosphereHasLaunchedTitle = 65001,
|
||||
};
|
||||
private:
|
||||
/* Actual commands. */
|
||||
Result GetTitleId(Out<u64> tid, u64 pid);
|
||||
|
||||
/* Atmosphere commands. */
|
||||
Result AtmosphereGetProcessId(Out<u64> pid, u64 tid);
|
||||
Result AtmosphereHasLaunchedTitle(Out<bool> 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),
|
||||
};
|
||||
};
|
44
stratosphere/pm/source/pm_info_service.cpp
Normal file
44
stratosphere/pm/source/pm_info_service.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stratosphere/ldr/ldr_pm_api.hpp>
|
||||
#include <stratosphere/pm.hpp>
|
||||
|
||||
#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<ncm::TitleId> out, u64 process_id) {
|
||||
return impl::GetTitleId(out.GetPointer(), process_id);
|
||||
}
|
||||
|
||||
/* Atmosphere extension commands. */
|
||||
Result InformationService::AtmosphereGetProcessId(Out<u64> out, ncm::TitleId title_id) {
|
||||
return impl::GetProcessId(out.GetPointer(), title_id);
|
||||
}
|
||||
|
||||
Result InformationService::AtmosphereHasLaunchedTitle(Out<bool> out, ncm::TitleId title_id) {
|
||||
return pm::info::HasLaunchedTitle(out.GetPointer(), title_id);
|
||||
}
|
||||
|
||||
}
|
49
stratosphere/pm/source/pm_info_service.hpp
Normal file
49
stratosphere/pm/source/pm_info_service.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/ldr.hpp>
|
||||
#include <stratosphere/pm.hpp>
|
||||
|
||||
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<ncm::TitleId> out, u64 process_id);
|
||||
|
||||
/* Atmosphere extension commands. */
|
||||
Result AtmosphereGetProcessId(Out<u64> out, ncm::TitleId title_id);
|
||||
Result AtmosphereHasLaunchedTitle(Out<bool> 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),
|
||||
};
|
||||
};
|
||||
|
||||
}
|
|
@ -22,14 +22,15 @@
|
|||
#include <switch.h>
|
||||
#include <atmosphere.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/cfg.hpp>
|
||||
#include <stratosphere/sm/sm_manager_api.hpp>
|
||||
|
||||
#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<ShellServiceDeprecated>("pm:shell", 3));
|
||||
s_server_manager.AddWaitable(new ServiceServer<DebugMonitorServiceDeprecated>("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<sts::pm::shell::ShellService>("pm:shell", 3));
|
||||
s_server_manager.AddWaitable(new ServiceServer<sts::pm::dmnt::DebugMonitorService>("pm:dmnt", 3));
|
||||
} else {
|
||||
s_server_manager.AddWaitable(new ServiceServer<ShellService>("pm:shell", 3));
|
||||
s_server_manager.AddWaitable(new ServiceServer<DebugMonitorService>("pm:dmnt", 3));
|
||||
s_server_manager.AddWaitable(new ServiceServer<sts::pm::shell::ShellServiceDeprecated>("pm:shell", 3));
|
||||
s_server_manager.AddWaitable(new ServiceServer<sts::pm::dmnt::DebugMonitorServiceDeprecated>("pm:dmnt", 3));
|
||||
}
|
||||
s_server_manager.AddWaitable(new ServiceServer<BootModeService>("pm:bm", 6));
|
||||
s_server_manager.AddWaitable(new ServiceServer<InformationService>("pm:info", 19));
|
||||
s_server_manager.AddWaitable(new ServiceServer<sts::pm::bm::BootModeService>("pm:bm", 6));
|
||||
s_server_manager.AddWaitable(new ServiceServer<sts::pm::info::InformationService>("pm:info", 19));
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
s_server_manager.Process();
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#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();
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <mutex>
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "pm_registration.hpp"
|
||||
|
||||
class ProcessWaiter final : public IWaitable {
|
||||
public:
|
||||
std::shared_ptr<Registration::Process> process;
|
||||
|
||||
ProcessWaiter(std::shared_ptr<Registration::Process> p) : process(p) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
std::shared_ptr<Registration::Process> 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<std::shared_ptr<Registration::Process>> processes;
|
||||
|
||||
void lock() {
|
||||
GetMutex()->lock();
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
GetMutex()->unlock();
|
||||
}
|
||||
|
||||
void try_lock() {
|
||||
GetMutex()->try_lock();
|
||||
}
|
||||
};
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/ldr.hpp>
|
||||
#include <stratosphere/ldr/ldr_pm_api.hpp>
|
||||
|
||||
#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<u64> 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<Registration::Process>(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<ProcessList &> lk(GetProcessList());
|
||||
LoaderProgramInfo program_info = {0};
|
||||
|
||||
std::shared_ptr<Registration::Process> 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<Registration::Process> 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<Registration::Process> process) {
|
||||
bool signal_debug_process_5x;
|
||||
|
||||
/* Scope to manage process list lock. */
|
||||
{
|
||||
std::scoped_lock<ProcessList &> 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<ProcessList &> 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<Registration::Process> process) {
|
||||
std::scoped_lock<ProcessList &> 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<ProcessList &> lk(GetProcessList());
|
||||
|
||||
/* Remove process from list. */
|
||||
for (unsigned int i = 0; i < g_process_list.processes.size(); i++) {
|
||||
std::shared_ptr<Registration::Process> 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<ProcessList &> 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<Registration::Process> *out) {
|
||||
std::scoped_lock<ProcessList &> 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::Process> Registration::GetProcess(u64 pid) {
|
||||
std::scoped_lock<ProcessList &> lk(GetProcessList());
|
||||
|
||||
for (auto &process : g_process_list.processes) {
|
||||
if (process->pid == pid) {
|
||||
return process;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Registration::Process> Registration::GetProcessByTitleId(u64 tid) {
|
||||
std::scoped_lock<ProcessList &> 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<ProcessList &> 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<ProcessList &> 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<ProcessList &> dead_lk(g_dead_process_list);
|
||||
|
||||
if (g_dead_process_list.processes.size()) {
|
||||
std::shared_ptr<Registration::Process> 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();
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
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> process);
|
||||
static void FinalizeExitedProcess(std::shared_ptr<Process> process);
|
||||
|
||||
static void AddProcessToList(std::shared_ptr<Process> process);
|
||||
static void RemoveProcessFromList(u64 pid);
|
||||
static void SetProcessState(u64 pid, ProcessState new_state);
|
||||
|
||||
static std::shared_ptr<Registration::Process> GetProcess(u64 pid);
|
||||
static std::shared_ptr<Registration::Process> 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<Process> *out = nullptr);
|
||||
};
|
||||
|
||||
#include "pm_process_wait.hpp"
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#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<u64> 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<ProcessList &> 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<ProcessList &> lk(Registration::GetProcessList());
|
||||
|
||||
auto proc = Registration::GetProcessByTitleId(tid);
|
||||
if (proc != NULL) {
|
||||
return svcTerminateProcess(proc->handle);
|
||||
} else {
|
||||
return ResultPmProcessNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
void ShellService::GetProcessWaitEvent(Out<CopiedHandle> event) {
|
||||
event.SetValue(Registration::GetProcessEventHandle());
|
||||
}
|
||||
|
||||
void ShellService::GetProcessEventType(Out<u64> type, Out<u64> pid) {
|
||||
Registration::GetProcessEventType(pid.GetPointer(), type.GetPointer());
|
||||
}
|
||||
|
||||
Result ShellService::FinalizeExitedProcess(u64 pid) {
|
||||
std::scoped_lock<ProcessList &> 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<ProcessList &> lk(Registration::GetProcessList());
|
||||
|
||||
auto proc = Registration::GetProcess(pid);
|
||||
if (proc != NULL) {
|
||||
proc->flags &= ~PROCESSFLAGS_CRASHED;
|
||||
return ResultSuccess;
|
||||
} else {
|
||||
return ResultPmProcessNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
void ShellService::NotifyBootFinished() {
|
||||
if (!g_has_boot_finished) {
|
||||
g_has_boot_finished = true;
|
||||
Registration::SignalBootFinished();
|
||||
EmbeddedBoot2::Main();
|
||||
}
|
||||
}
|
||||
|
||||
Result ShellService::GetApplicationProcessId(Out<u64> pid) {
|
||||
std::scoped_lock<ProcessList &> lk(Registration::GetProcessList());
|
||||
|
||||
std::shared_ptr<Registration::Process> app_proc;
|
||||
if (Registration::HasApplicationProcess(&app_proc)) {
|
||||
pid.SetValue(app_proc->pid);
|
||||
return ResultSuccess;
|
||||
}
|
||||
return ResultPmProcessNotFound;
|
||||
}
|
||||
|
||||
Result ShellService::BoostSystemMemoryResourceLimit(u64 sysmem_size) {
|
||||
return ResourceLimitUtils::BoostSystemMemoryResourceLimit(sysmem_size);
|
||||
}
|
||||
|
||||
Result ShellService::BoostSystemThreadsResourceLimit() {
|
||||
/* Starting in 7.0.0, Nintendo reduces the number of system threads from 0x260 to 0x60, */
|
||||
/* Until this command is called to double that amount to 0xC0. */
|
||||
/* We will simply not reduce the number of system threads available for no reason. */
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
|
||||
void ShellService::GetBootFinishedEvent(Out<CopiedHandle> event) {
|
||||
/* In 8.0.0, Nintendo added this command, which signals that the boot sysmodule has finished. */
|
||||
/* Nintendo only signals it in safe mode FIRM, and this function aborts on normal FIRM. */
|
||||
/* We will signal it always, but only allow this function to succeed on safe mode. */
|
||||
{
|
||||
u64 is_recovery_boot = 0;
|
||||
R_ASSERT(SmcGetConfig(SplConfigItem_IsRecoveryBoot, &is_recovery_boot));
|
||||
if (!is_recovery_boot) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
event.SetValue(Registration::GetBootFinishedEventHandle());
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "pm_registration.hpp"
|
||||
|
||||
/* Represents modern ShellService (5.0.0+) */
|
||||
class ShellService : public IServiceObject {
|
||||
private:
|
||||
enum class CommandId {
|
||||
LaunchProcess = 0,
|
||||
TerminateProcessId = 1,
|
||||
TerminateTitleId = 2,
|
||||
GetProcessWaitEvent = 3,
|
||||
GetProcessEventType = 4,
|
||||
NotifyBootFinished = 5,
|
||||
GetApplicationProcessId = 6,
|
||||
BoostSystemMemoryResourceLimit = 7,
|
||||
BoostSystemThreadsResourceLimit = 8,
|
||||
GetBootFinishedEvent = 9,
|
||||
};
|
||||
protected:
|
||||
/* Actual commands. */
|
||||
Result LaunchProcess(Out<u64> pid, Registration::TidSid tid_sid, u32 launch_flags);
|
||||
Result TerminateProcessId(u64 pid);
|
||||
Result TerminateTitleId(u64 tid);
|
||||
void GetProcessWaitEvent(Out<CopiedHandle> event);
|
||||
void GetProcessEventType(Out<u64> type, Out<u64> pid);
|
||||
Result FinalizeExitedProcess(u64 pid);
|
||||
Result ClearProcessNotificationFlag(u64 pid);
|
||||
void NotifyBootFinished();
|
||||
Result GetApplicationProcessId(Out<u64> pid);
|
||||
Result BoostSystemMemoryResourceLimit(u64 sysmem_size);
|
||||
Result BoostSystemThreadsResourceLimit();
|
||||
void GetBootFinishedEvent(Out<CopiedHandle> event);
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
/* 5.0.0-* */
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, LaunchProcess),
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, TerminateProcessId),
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, TerminateTitleId),
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, GetProcessWaitEvent),
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, GetProcessEventType),
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, NotifyBootFinished),
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, GetApplicationProcessId),
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, BoostSystemMemoryResourceLimit),
|
||||
|
||||
/* 7.0.0-* */
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, BoostSystemThreadsResourceLimit, FirmwareVersion_700),
|
||||
|
||||
/* 8.0.0-* */
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, GetBootFinishedEvent, FirmwareVersion_800),
|
||||
};
|
||||
};
|
||||
|
||||
/* Represents deprecated ShellService (1.0.0-4.1.0). */
|
||||
class ShellServiceDeprecated : public ShellService {
|
||||
private:
|
||||
enum class CommandId {
|
||||
LaunchProcess = 0,
|
||||
TerminateProcessId = 1,
|
||||
TerminateTitleId = 2,
|
||||
GetProcessWaitEvent = 3,
|
||||
GetProcessEventType = 4,
|
||||
FinalizeExitedProcess = 5,
|
||||
ClearProcessNotificationFlag = 6,
|
||||
NotifyBootFinished = 7,
|
||||
GetApplicationProcessId = 8,
|
||||
BoostSystemMemoryResourceLimit = 9,
|
||||
};
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
/* 1.0.0- */
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, LaunchProcess),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, TerminateProcessId),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, TerminateTitleId),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, GetProcessWaitEvent),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, GetProcessEventType),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, FinalizeExitedProcess),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, ClearProcessNotificationFlag),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, NotifyBootFinished),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, GetApplicationProcessId),
|
||||
|
||||
/* 4.0.0- */
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, BoostSystemMemoryResourceLimit, FirmwareVersion_400),
|
||||
};
|
||||
};
|
76
stratosphere/pm/source/pm_shell_service.cpp
Normal file
76
stratosphere/pm/source/pm_shell_service.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pm_shell_service.hpp"
|
||||
#include "impl/pm_process_manager.hpp"
|
||||
|
||||
namespace sts::pm::shell {
|
||||
|
||||
/* Overrides for libstratosphere pm::shell commands. */
|
||||
Result LaunchTitle(u64 *out_process_id, const ncm::TitleLocation &loc, u32 launch_flags) {
|
||||
return impl::LaunchTitle(out_process_id, loc, launch_flags);
|
||||
}
|
||||
|
||||
/* Service command implementations. */
|
||||
Result ShellServiceBase::LaunchTitle(Out<u64> out_process_id, ncm::TitleLocation loc, u32 flags) {
|
||||
return pm::shell::LaunchTitle(out_process_id.GetPointer(), loc, flags);
|
||||
}
|
||||
|
||||
Result ShellServiceBase::TerminateProcess(u64 process_id) {
|
||||
return impl::TerminateProcess(process_id);
|
||||
}
|
||||
|
||||
Result ShellServiceBase::TerminateTitle(ncm::TitleId title_id) {
|
||||
return impl::TerminateTitle(title_id);
|
||||
}
|
||||
|
||||
void ShellServiceBase::GetProcessEventHandle(Out<CopiedHandle> out) {
|
||||
R_ASSERT(impl::GetProcessEventHandle(out.GetHandlePointer()));
|
||||
}
|
||||
|
||||
void ShellServiceBase::GetProcessEventInfo(Out<ProcessEventInfo> out) {
|
||||
R_ASSERT(impl::GetProcessEventInfo(out.GetPointer()));
|
||||
}
|
||||
|
||||
Result ShellServiceBase::CleanupProcess(u64 process_id) {
|
||||
return impl::CleanupProcess(process_id);
|
||||
}
|
||||
|
||||
Result ShellServiceBase::ClearExceptionOccurred(u64 process_id) {
|
||||
return impl::ClearExceptionOccurred(process_id);
|
||||
}
|
||||
|
||||
void ShellServiceBase::NotifyBootFinished() {
|
||||
R_ASSERT(impl::NotifyBootFinished());
|
||||
}
|
||||
|
||||
Result ShellServiceBase::GetApplicationProcessIdForShell(Out<u64> out) {
|
||||
return impl::GetApplicationProcessId(out.GetPointer());
|
||||
}
|
||||
|
||||
Result ShellServiceBase::BoostSystemMemoryResourceLimit(u64 boost_size) {
|
||||
return impl::BoostSystemMemoryResourceLimit(boost_size);
|
||||
}
|
||||
|
||||
Result ShellServiceBase::BoostSystemThreadResourceLimit() {
|
||||
return impl::BoostSystemThreadResourceLimit();
|
||||
}
|
||||
|
||||
void ShellServiceBase::GetBootFinishedEventHandle(Out<CopiedHandle> out) {
|
||||
R_ASSERT(impl::GetBootFinishedEventHandle(out.GetHandlePointer()));
|
||||
}
|
||||
|
||||
}
|
114
stratosphere/pm/source/pm_shell_service.hpp
Normal file
114
stratosphere/pm/source/pm_shell_service.hpp
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/ldr.hpp>
|
||||
#include <stratosphere/pm.hpp>
|
||||
|
||||
namespace sts::pm::shell {
|
||||
|
||||
class ShellServiceBase : public IServiceObject {
|
||||
protected:
|
||||
/* Actual command implementations. */
|
||||
virtual Result LaunchTitle(Out<u64> out_process_id, ncm::TitleLocation loc, u32 flags);
|
||||
virtual Result TerminateProcess(u64 process_id);
|
||||
virtual Result TerminateTitle(ncm::TitleId title_id);
|
||||
virtual void GetProcessEventHandle(Out<CopiedHandle> out);
|
||||
virtual void GetProcessEventInfo(Out<ProcessEventInfo> out);
|
||||
virtual Result CleanupProcess(u64 process_id);
|
||||
virtual Result ClearExceptionOccurred(u64 process_id);
|
||||
virtual void NotifyBootFinished();
|
||||
virtual Result GetApplicationProcessIdForShell(Out<u64> out);
|
||||
virtual Result BoostSystemMemoryResourceLimit(u64 boost_size);
|
||||
virtual Result BoostSystemThreadResourceLimit();
|
||||
virtual void GetBootFinishedEventHandle(Out<CopiedHandle> out);
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
/* No entries, because ShellServiceBase is abstract. */
|
||||
};
|
||||
};
|
||||
|
||||
/* This represents modern ShellService (5.0.0+). */
|
||||
class ShellService final : public ShellServiceBase {
|
||||
private:
|
||||
enum class CommandId {
|
||||
LaunchTitle = 0,
|
||||
TerminateProcess = 1,
|
||||
TerminateTitle = 2,
|
||||
GetProcessEventHandle = 3,
|
||||
GetProcessEventInfo = 4,
|
||||
NotifyBootFinished = 5,
|
||||
GetApplicationProcessIdForShell = 6,
|
||||
BoostSystemMemoryResourceLimit = 7,
|
||||
BoostSystemThreadResourceLimit = 8,
|
||||
GetBootFinishedEventHandle = 9,
|
||||
};
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
/* 5.0.0-* */
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, LaunchTitle),
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, TerminateProcess),
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, TerminateTitle),
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, GetProcessEventHandle),
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, GetProcessEventInfo),
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, NotifyBootFinished),
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, GetApplicationProcessIdForShell),
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, BoostSystemMemoryResourceLimit),
|
||||
|
||||
/* 7.0.0-* */
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, BoostSystemThreadResourceLimit, FirmwareVersion_700),
|
||||
|
||||
/* 8.0.0-* */
|
||||
MAKE_SERVICE_COMMAND_META(ShellService, GetBootFinishedEventHandle, FirmwareVersion_800),
|
||||
};
|
||||
};
|
||||
|
||||
/* This represents deprecated ShellService (1.0.0-4.1.0). */
|
||||
class ShellServiceDeprecated final : public ShellServiceBase {
|
||||
private:
|
||||
enum class CommandId {
|
||||
LaunchTitle = 0,
|
||||
TerminateProcess = 1,
|
||||
TerminateTitle = 2,
|
||||
GetProcessEventHandle = 3,
|
||||
GetProcessEventInfo = 4,
|
||||
CleanupProcess = 5,
|
||||
ClearExceptionOccurred = 6,
|
||||
NotifyBootFinished = 7,
|
||||
GetApplicationProcessIdForShell = 8,
|
||||
BoostSystemMemoryResourceLimit = 9,
|
||||
};
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
/* 1.0.0-4.1.0 */
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, LaunchTitle),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, TerminateProcess),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, TerminateTitle),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, GetProcessEventHandle),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, GetProcessEventInfo),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, CleanupProcess),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, ClearExceptionOccurred),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, NotifyBootFinished),
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, GetApplicationProcessIdForShell),
|
||||
|
||||
/* 4.0.0-4.1.0 */
|
||||
MAKE_SERVICE_COMMAND_META(ShellServiceDeprecated, BoostSystemMemoryResourceLimit),
|
||||
};
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in a new issue