2019-06-29 10:20:36 +01:00
|
|
|
/*
|
2021-10-04 20:59:10 +01:00
|
|
|
* Copyright (c) Atmosphère-NX
|
2019-06-29 10:20:36 +01:00
|
|
|
*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
2020-05-11 23:02:10 +01:00
|
|
|
#include <stratosphere.hpp>
|
2023-10-26 22:44:32 +01:00
|
|
|
#include "pm_spec.hpp"
|
2019-06-29 10:20:36 +01:00
|
|
|
|
2023-10-26 22:44:32 +01:00
|
|
|
namespace ams::pm::impl {
|
2019-06-29 10:20:36 +01:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2020-03-29 23:24:40 +01:00
|
|
|
constexpr svc::LimitableResource LimitableResources[] = {
|
|
|
|
svc::LimitableResource_PhysicalMemoryMax,
|
|
|
|
svc::LimitableResource_ThreadCountMax,
|
|
|
|
svc::LimitableResource_EventCountMax,
|
|
|
|
svc::LimitableResource_TransferMemoryCountMax,
|
|
|
|
svc::LimitableResource_SessionCountMax,
|
2019-06-29 10:20:36 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Definitions for limit differences over time. */
|
2020-01-03 01:43:17 +00:00
|
|
|
constexpr size_t ExtraSystemMemorySize400 = 10_MB;
|
|
|
|
constexpr size_t ReservedMemorySize600 = 5_MB;
|
2019-06-29 10:20:36 +01:00
|
|
|
|
2019-12-10 01:56:38 +00:00
|
|
|
/* Atmosphere always allocates extra memory for system usage. */
|
2023-01-25 02:54:28 +00:00
|
|
|
constexpr size_t ExtraSystemMemorySizeAtmosphere = 32_MB;
|
2019-06-29 10:20:36 +01:00
|
|
|
|
2022-03-22 06:52:16 +00:00
|
|
|
/* Desired extra threads. */
|
|
|
|
constexpr u64 BaseApplicationThreads = 96;
|
|
|
|
constexpr u64 BaseAppletThreads = 96;
|
|
|
|
constexpr u64 BaseSystemThreads = 800 - BaseAppletThreads - BaseApplicationThreads;
|
|
|
|
|
|
|
|
constexpr s64 ExtraSystemThreads = 1024 - BaseSystemThreads;
|
|
|
|
constexpr s64 ExtraApplicationThreads = 256 - BaseApplicationThreads;
|
|
|
|
constexpr s64 ExtraAppletThreads = 256 - BaseAppletThreads;
|
|
|
|
|
|
|
|
static_assert(ExtraSystemThreads >= 0);
|
|
|
|
static_assert(ExtraApplicationThreads >= 0);
|
|
|
|
static_assert(ExtraAppletThreads >= 0);
|
|
|
|
|
2019-06-29 10:20:36 +01:00
|
|
|
/* Globals. */
|
2021-09-30 06:52:50 +01:00
|
|
|
constinit os::SdkMutex g_resource_limit_lock;
|
2021-10-05 01:12:32 +01:00
|
|
|
constinit os::NativeHandle g_resource_limit_handles[ResourceLimitGroup_Count];
|
2021-09-30 06:52:50 +01:00
|
|
|
constinit spl::MemoryArrangement g_memory_arrangement = spl::MemoryArrangement_Standard;
|
2022-03-22 06:52:16 +00:00
|
|
|
constinit u64 g_extra_threads_available[ResourceLimitGroup_Count];
|
2019-06-29 10:20:36 +01:00
|
|
|
|
2023-05-14 11:06:52 +01:00
|
|
|
constinit os::SdkMutex g_system_memory_boost_lock;
|
|
|
|
constinit u64 g_system_memory_boost_size = 0;
|
|
|
|
constinit u64 g_system_memory_boost_size_for_mitm = 0;
|
|
|
|
|
|
|
|
ALWAYS_INLINE u64 GetCurrentSystemMemoryBoostSize() {
|
|
|
|
return g_system_memory_boost_size + g_system_memory_boost_size_for_mitm;
|
|
|
|
}
|
|
|
|
|
2021-09-30 06:52:50 +01:00
|
|
|
constinit u64 g_resource_limits[ResourceLimitGroup_Count][svc::LimitableResource_Count] = {
|
2019-06-29 10:20:36 +01:00
|
|
|
[ResourceLimitGroup_System] = {
|
2021-09-30 07:33:25 +01:00
|
|
|
[svc::LimitableResource_PhysicalMemoryMax] = 0, /* Initialized dynamically later. */
|
2022-03-22 06:52:16 +00:00
|
|
|
[svc::LimitableResource_ThreadCountMax] = BaseSystemThreads,
|
2021-09-30 07:33:25 +01:00
|
|
|
[svc::LimitableResource_EventCountMax] = 0, /* Initialized dynamically later. */
|
|
|
|
[svc::LimitableResource_TransferMemoryCountMax] = 0, /* Initialized dynamically later. */
|
|
|
|
[svc::LimitableResource_SessionCountMax] = 0, /* Initialized dynamically later. */
|
2019-06-29 10:20:36 +01:00
|
|
|
},
|
|
|
|
[ResourceLimitGroup_Application] = {
|
2021-09-30 07:33:25 +01:00
|
|
|
[svc::LimitableResource_PhysicalMemoryMax] = 0, /* Initialized dynamically later. */
|
2022-03-22 06:52:16 +00:00
|
|
|
[svc::LimitableResource_ThreadCountMax] = BaseApplicationThreads,
|
2020-03-29 23:24:40 +01:00
|
|
|
[svc::LimitableResource_EventCountMax] = 0,
|
|
|
|
[svc::LimitableResource_TransferMemoryCountMax] = 32,
|
|
|
|
[svc::LimitableResource_SessionCountMax] = 1,
|
2019-06-29 10:20:36 +01:00
|
|
|
},
|
|
|
|
[ResourceLimitGroup_Applet] = {
|
2021-09-30 07:33:25 +01:00
|
|
|
[svc::LimitableResource_PhysicalMemoryMax] = 0, /* Initialized dynamically later. */
|
2022-03-22 06:52:16 +00:00
|
|
|
[svc::LimitableResource_ThreadCountMax] = BaseAppletThreads,
|
2020-03-29 23:24:40 +01:00
|
|
|
[svc::LimitableResource_EventCountMax] = 0,
|
|
|
|
[svc::LimitableResource_TransferMemoryCountMax] = 32,
|
|
|
|
[svc::LimitableResource_SessionCountMax] = 5,
|
2019-06-29 10:20:36 +01:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2021-09-30 06:52:50 +01:00
|
|
|
constinit u64 g_memory_resource_limits[spl::MemoryArrangement_Count][ResourceLimitGroup_Count] = {
|
2019-06-29 10:20:36 +01:00
|
|
|
[spl::MemoryArrangement_Standard] = {
|
2020-01-03 01:43:17 +00:00
|
|
|
[ResourceLimitGroup_System] = 269_MB,
|
|
|
|
[ResourceLimitGroup_Application] = 3285_MB,
|
|
|
|
[ResourceLimitGroup_Applet] = 535_MB,
|
2019-06-29 10:20:36 +01:00
|
|
|
},
|
|
|
|
[spl::MemoryArrangement_StandardForAppletDev] = {
|
2020-01-03 01:43:17 +00:00
|
|
|
[ResourceLimitGroup_System] = 481_MB,
|
|
|
|
[ResourceLimitGroup_Application] = 2048_MB,
|
|
|
|
[ResourceLimitGroup_Applet] = 1560_MB,
|
2019-06-29 10:20:36 +01:00
|
|
|
},
|
|
|
|
[spl::MemoryArrangement_StandardForSystemDev] = {
|
2020-01-03 01:43:17 +00:00
|
|
|
[ResourceLimitGroup_System] = 328_MB,
|
|
|
|
[ResourceLimitGroup_Application] = 3285_MB,
|
|
|
|
[ResourceLimitGroup_Applet] = 476_MB,
|
2019-06-29 10:20:36 +01:00
|
|
|
},
|
|
|
|
[spl::MemoryArrangement_Expanded] = {
|
2020-01-03 01:43:17 +00:00
|
|
|
[ResourceLimitGroup_System] = 653_MB,
|
|
|
|
[ResourceLimitGroup_Application] = 4916_MB,
|
|
|
|
[ResourceLimitGroup_Applet] = 568_MB,
|
2019-06-29 10:20:36 +01:00
|
|
|
},
|
|
|
|
[spl::MemoryArrangement_ExpandedForAppletDev] = {
|
2020-01-03 01:43:17 +00:00
|
|
|
[ResourceLimitGroup_System] = 653_MB,
|
|
|
|
[ResourceLimitGroup_Application] = 3285_MB,
|
|
|
|
[ResourceLimitGroup_Applet] = 2199_MB,
|
2019-06-29 10:20:36 +01:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Helpers. */
|
|
|
|
Result SetMemoryResourceLimitLimitValue(ResourceLimitGroup group, u64 new_memory_limit) {
|
2020-03-29 23:24:40 +01:00
|
|
|
const u64 old_memory_limit = g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax];
|
|
|
|
g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax] = new_memory_limit;
|
2019-10-24 09:40:44 +01:00
|
|
|
|
2022-02-14 22:45:32 +00:00
|
|
|
/* Restore the old memory limit if we fail. */
|
|
|
|
ON_RESULT_FAILURE { g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax] = old_memory_limit; };
|
2019-10-24 09:40:44 +01:00
|
|
|
|
2022-02-14 22:45:32 +00:00
|
|
|
/* Set the resource limit. */
|
|
|
|
R_RETURN(svc::SetResourceLimitLimitValue(GetResourceLimitHandle(group), svc::LimitableResource_PhysicalMemoryMax, g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax]));
|
2019-06-29 10:20:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Result SetResourceLimitLimitValues(ResourceLimitGroup group, u64 new_memory_limit) {
|
|
|
|
/* First, set memory limit. */
|
|
|
|
R_TRY(SetMemoryResourceLimitLimitValue(group, new_memory_limit));
|
|
|
|
|
|
|
|
/* Set other limit values. */
|
2020-03-29 23:24:40 +01:00
|
|
|
for (size_t i = 0; i < svc::LimitableResource_Count; i++) {
|
2019-06-29 10:20:36 +01:00
|
|
|
const auto resource = LimitableResources[i];
|
2020-03-29 23:24:40 +01:00
|
|
|
if (resource == svc::LimitableResource_PhysicalMemoryMax) {
|
2019-06-29 10:20:36 +01:00
|
|
|
continue;
|
|
|
|
}
|
2020-03-29 23:24:40 +01:00
|
|
|
R_TRY(svc::SetResourceLimitLimitValue(GetResourceLimitHandle(group), resource, g_resource_limits[group][resource]));
|
2019-06-29 10:20:36 +01:00
|
|
|
}
|
2022-03-22 06:52:16 +00:00
|
|
|
|
|
|
|
R_SUCCEED();
|
2019-06-29 10:20:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2021-10-05 01:12:32 +01:00
|
|
|
const auto reslimit_hnd = GetResourceLimitHandle(group);
|
2020-03-29 23:24:40 +01:00
|
|
|
for (size_t i = 0; i < svc::LimitableResource_Count; i++) {
|
2019-06-29 10:20:36 +01:00
|
|
|
const auto resource = LimitableResources[i];
|
|
|
|
|
2020-03-29 23:24:40 +01:00
|
|
|
s64 value = 0;
|
2019-06-29 10:20:36 +01:00
|
|
|
while (true) {
|
2020-03-29 23:24:40 +01:00
|
|
|
R_ABORT_UNLESS(svc::GetResourceLimitCurrentValue(&value, reslimit_hnd, resource));
|
2019-06-29 10:20:36 +01:00
|
|
|
if (value == 0) {
|
|
|
|
break;
|
|
|
|
}
|
2021-09-30 07:33:25 +01:00
|
|
|
os::SleepThread(TimeSpan::FromMilliSeconds(1));
|
2019-06-29 10:20:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaitApplicationMemoryAvailable() {
|
2023-10-17 19:25:35 +01:00
|
|
|
/* Get firmware version. */
|
|
|
|
const auto fw_ver = hos::GetVersion();
|
|
|
|
|
|
|
|
/* On 15.0.0+, pm considers application memory to be available if there is exactly 96 MB outstanding. */
|
|
|
|
/* This is probably because this corresponds to the gameplay-recording memory. */
|
|
|
|
constexpr u64 AllowedUsedApplicationMemory = 96_MB;
|
|
|
|
|
|
|
|
/* Wait for memory to be available. */
|
2019-06-29 10:20:36 +01:00
|
|
|
u64 value = 0;
|
|
|
|
while (true) {
|
2021-09-30 07:33:25 +01:00
|
|
|
R_ABORT_UNLESS(svc::GetSystemInfo(&value, svc::SystemInfoType_UsedPhysicalMemorySize, svc::InvalidHandle, svc::PhysicalMemorySystemInfo_Application));
|
2023-10-17 19:25:35 +01:00
|
|
|
if (value == 0 || (fw_ver >= hos::Version_15_0_0 && value == AllowedUsedApplicationMemory)) {
|
2019-06-29 10:20:36 +01:00
|
|
|
break;
|
|
|
|
}
|
2021-09-30 07:33:25 +01:00
|
|
|
os::SleepThread(TimeSpan::FromMilliSeconds(1));
|
2019-06-29 10:20:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-26 02:10:58 +01:00
|
|
|
bool IsKTraceEnabled() {
|
|
|
|
u64 value = 0;
|
2021-09-30 07:33:25 +01:00
|
|
|
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), svc::InfoType_MesosphereMeta, svc::InvalidHandle, svc::MesosphereMetaInfo_IsKTraceEnabled));
|
2020-08-26 02:10:58 +01:00
|
|
|
|
|
|
|
return value != 0;
|
|
|
|
}
|
|
|
|
|
2022-03-22 06:52:16 +00:00
|
|
|
ALWAYS_INLINE Result BoostThreadResourceLimitLocked(ResourceLimitGroup group) {
|
|
|
|
AMS_ASSERT(g_resource_limit_lock.IsLockedByCurrentThread());
|
|
|
|
|
|
|
|
/* Set new limit. */
|
|
|
|
const s64 new_thread_count = g_resource_limits[group][svc::LimitableResource_ThreadCountMax] + g_extra_threads_available[group];
|
|
|
|
R_TRY(svc::SetResourceLimitLimitValue(GetResourceLimitHandle(group), svc::LimitableResource_ThreadCountMax, new_thread_count));
|
|
|
|
|
|
|
|
/* Record that we did so. */
|
|
|
|
g_resource_limits[group][svc::LimitableResource_ThreadCountMax] = new_thread_count;
|
|
|
|
g_extra_threads_available[group] = 0;
|
|
|
|
|
|
|
|
R_SUCCEED();
|
|
|
|
}
|
|
|
|
|
2023-10-26 22:44:32 +01:00
|
|
|
template<auto ImplFunction>
|
|
|
|
ALWAYS_INLINE Result GetResourceLimitValueImpl(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
|
2022-03-22 06:52:16 +00:00
|
|
|
/* Sanity check group. */
|
|
|
|
AMS_ABORT_UNLESS(group < ResourceLimitGroup_Count);
|
|
|
|
|
|
|
|
/* Get handle. */
|
|
|
|
const auto handle = GetResourceLimitHandle(group);
|
|
|
|
|
|
|
|
/* Get values. */
|
|
|
|
int64_t values[svc::LimitableResource_Count];
|
2023-10-26 22:44:32 +01:00
|
|
|
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_PhysicalMemoryMax]), handle, svc::LimitableResource_PhysicalMemoryMax));
|
|
|
|
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_ThreadCountMax]), handle, svc::LimitableResource_ThreadCountMax));
|
|
|
|
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_EventCountMax]), handle, svc::LimitableResource_EventCountMax));
|
|
|
|
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_TransferMemoryCountMax]), handle, svc::LimitableResource_TransferMemoryCountMax));
|
|
|
|
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_SessionCountMax]), handle, svc::LimitableResource_SessionCountMax));
|
2022-03-22 06:52:16 +00:00
|
|
|
|
|
|
|
/* Set to output. */
|
|
|
|
out->physical_memory = values[svc::LimitableResource_PhysicalMemoryMax];
|
|
|
|
out->thread_count = values[svc::LimitableResource_ThreadCountMax];
|
|
|
|
out->event_count = values[svc::LimitableResource_EventCountMax];
|
|
|
|
out->transfer_memory_count = values[svc::LimitableResource_TransferMemoryCountMax];
|
|
|
|
out->session_count = values[svc::LimitableResource_SessionCountMax];
|
|
|
|
|
|
|
|
R_SUCCEED();
|
|
|
|
}
|
|
|
|
|
2023-05-14 11:06:52 +01:00
|
|
|
Result BoostSystemMemoryResourceLimitLocked(u64 normal_boost, u64 mitm_boost) {
|
|
|
|
/* Check pre-conditions. */
|
|
|
|
AMS_ASSERT(g_system_memory_boost_lock.IsLockedByCurrentThread());
|
|
|
|
|
|
|
|
/* Determine total boost. */
|
|
|
|
const u64 boost_size = normal_boost + mitm_boost;
|
|
|
|
|
|
|
|
/* Don't allow all application memory to be taken away. */
|
2024-01-20 20:09:04 +00:00
|
|
|
R_UNLESS(boost_size < g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application], pm::ResultInvalidSize());
|
2023-05-14 11:06:52 +01:00
|
|
|
|
|
|
|
const u64 new_app_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application] - boost_size;
|
|
|
|
{
|
|
|
|
std::scoped_lock lk(g_resource_limit_lock);
|
|
|
|
|
2024-01-20 20:09:04 +00:00
|
|
|
const auto cur_boost_size = GetCurrentSystemMemoryBoostSize();
|
|
|
|
|
2023-05-14 11:06:52 +01:00
|
|
|
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
|
|
|
/* Starting in 5.0.0, PM does not allow for only one of the sets to fail. */
|
2024-01-20 20:09:04 +00:00
|
|
|
if (boost_size < cur_boost_size) {
|
2023-05-14 11:06:52 +01:00
|
|
|
R_TRY(svc::SetUnsafeLimit(boost_size));
|
|
|
|
R_ABORT_UNLESS(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
|
2024-01-20 20:09:04 +00:00
|
|
|
} else if (boost_size > cur_boost_size) {
|
2023-05-14 11:06:52 +01:00
|
|
|
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
|
|
|
|
R_ABORT_UNLESS(svc::SetUnsafeLimit(boost_size));
|
2024-01-20 20:09:04 +00:00
|
|
|
} else {
|
|
|
|
/* If the boost size is equal, there's nothing to do. */
|
2023-05-14 11:06:52 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const u64 new_sys_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_System] + boost_size;
|
2024-01-20 20:09:04 +00:00
|
|
|
if (boost_size < cur_boost_size) {
|
2023-05-14 11:06:52 +01:00
|
|
|
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System, new_sys_size));
|
|
|
|
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
|
2024-01-20 20:09:04 +00:00
|
|
|
} else if (boost_size > cur_boost_size) {
|
2023-05-14 11:06:52 +01:00
|
|
|
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
|
|
|
|
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System, new_sys_size));
|
2024-01-20 20:09:04 +00:00
|
|
|
} else {
|
|
|
|
/* If the boost size is equal, there's nothing to do. */
|
2023-05-14 11:06:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_system_memory_boost_size = normal_boost;
|
|
|
|
g_system_memory_boost_size_for_mitm = mitm_boost;
|
|
|
|
}
|
|
|
|
|
|
|
|
R_SUCCEED();
|
|
|
|
}
|
|
|
|
|
2019-06-29 10:20:36 +01:00
|
|
|
}
|
|
|
|
|
2023-10-26 22:44:32 +01:00
|
|
|
Result InitializeSpec() {
|
2019-06-29 10:20:36 +01:00
|
|
|
/* Create resource limit handles. */
|
|
|
|
for (size_t i = 0; i < ResourceLimitGroup_Count; i++) {
|
|
|
|
if (i == ResourceLimitGroup_System) {
|
|
|
|
u64 value = 0;
|
2020-03-29 23:24:40 +01:00
|
|
|
R_ABORT_UNLESS(svc::GetInfo(&value, svc::InfoType_ResourceLimit, svc::InvalidHandle, 0));
|
|
|
|
g_resource_limit_handles[i] = static_cast<svc::Handle>(value);
|
2019-06-29 10:20:36 +01:00
|
|
|
} else {
|
2021-10-09 20:45:31 +01:00
|
|
|
R_ABORT_UNLESS(svc::CreateResourceLimit(g_resource_limit_handles + i));
|
2019-06-29 10:20:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-30 07:33:25 +01:00
|
|
|
/* Adjust memory limits based on hos firmware version. */
|
2019-10-15 06:49:06 +01:00
|
|
|
const auto hos_version = hos::GetVersion();
|
2020-04-14 06:19:44 +01:00
|
|
|
if (hos_version >= hos::Version_4_0_0) {
|
2021-09-30 07:33:25 +01:00
|
|
|
/* 4.0.0 took memory away from applet and gave it to system, for the Standard and StandardForSystemDev profiles. */
|
2019-06-29 10:20:36 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-09-30 07:33:25 +01:00
|
|
|
/* Determine system resource counts. */
|
|
|
|
{
|
|
|
|
/* Get the total resource counts. */
|
|
|
|
s64 total_events, total_transfer_memories, total_sessions;
|
|
|
|
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(std::addressof(total_events), GetResourceLimitHandle(ResourceLimitGroup_System), svc::LimitableResource_EventCountMax));
|
|
|
|
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(std::addressof(total_transfer_memories), GetResourceLimitHandle(ResourceLimitGroup_System), svc::LimitableResource_TransferMemoryCountMax));
|
|
|
|
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(std::addressof(total_sessions), GetResourceLimitHandle(ResourceLimitGroup_System), svc::LimitableResource_SessionCountMax));
|
|
|
|
|
|
|
|
/* Determine system counts. */
|
|
|
|
const s64 sys_events = total_events - (g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_EventCountMax] + g_resource_limits[ResourceLimitGroup_Applet][svc::LimitableResource_EventCountMax]);
|
|
|
|
const s64 sys_transfer_memories = total_transfer_memories - (g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_TransferMemoryCountMax] + g_resource_limits[ResourceLimitGroup_Applet][svc::LimitableResource_TransferMemoryCountMax]);
|
|
|
|
const s64 sys_sessions = total_sessions - (g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_SessionCountMax] + g_resource_limits[ResourceLimitGroup_Applet][svc::LimitableResource_SessionCountMax]);
|
|
|
|
|
|
|
|
/* Check system counts. */
|
|
|
|
AMS_ABORT_UNLESS(sys_events >= 0);
|
|
|
|
AMS_ABORT_UNLESS(sys_transfer_memories >= 0);
|
|
|
|
AMS_ABORT_UNLESS(sys_sessions >= 0);
|
|
|
|
|
|
|
|
/* Set system counts. */
|
|
|
|
g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_EventCountMax] = sys_events;
|
|
|
|
g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_TransferMemoryCountMax] = sys_transfer_memories;
|
|
|
|
g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_SessionCountMax] = sys_sessions;
|
|
|
|
}
|
2019-07-03 05:27:53 +01:00
|
|
|
|
2021-09-30 07:33:25 +01:00
|
|
|
/* Determine extra application threads. */
|
|
|
|
{
|
|
|
|
/* Get total threads available. */
|
|
|
|
s64 total_threads;
|
|
|
|
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(std::addressof(total_threads), GetResourceLimitHandle(ResourceLimitGroup_System), svc::LimitableResource_ThreadCountMax));
|
2019-07-03 05:27:53 +01:00
|
|
|
|
2021-09-30 07:33:25 +01:00
|
|
|
/* Check that we have enough threads. */
|
|
|
|
const s64 required_threads = g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_ThreadCountMax] + g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_ThreadCountMax] + g_resource_limits[ResourceLimitGroup_Applet][svc::LimitableResource_ThreadCountMax];
|
|
|
|
AMS_ABORT_UNLESS(total_threads >= required_threads);
|
2019-07-03 05:27:53 +01:00
|
|
|
|
2022-03-22 06:52:16 +00:00
|
|
|
/* Set the number of extra threads. */
|
|
|
|
const s64 extra_threads = total_threads - required_threads;
|
|
|
|
if constexpr (true /* TODO: Should we expose the old "all extra threads are application" behavior? Seems pointless. */) {
|
|
|
|
if (extra_threads > 0) {
|
|
|
|
/* If we have any extra threads at all, require that we have enough. */
|
|
|
|
AMS_ABORT_UNLESS(extra_threads >= (ExtraSystemThreads + ExtraApplicationThreads + ExtraAppletThreads));
|
|
|
|
|
|
|
|
g_extra_threads_available[ResourceLimitGroup_System] += ExtraSystemThreads;
|
|
|
|
g_extra_threads_available[ResourceLimitGroup_Application] += ExtraApplicationThreads;
|
|
|
|
g_extra_threads_available[ResourceLimitGroup_Applet] += ExtraAppletThreads;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
g_extra_threads_available[ResourceLimitGroup_Application] = extra_threads;
|
|
|
|
}
|
2019-07-03 05:27:53 +01:00
|
|
|
}
|
2019-06-29 10:20:36 +01:00
|
|
|
|
|
|
|
/* Choose and initialize memory arrangement. */
|
2021-09-05 18:08:30 +01:00
|
|
|
const bool use_dynamic_memory_arrangement = (hos_version >= hos::Version_5_0_0);
|
2020-08-21 10:38:16 +01:00
|
|
|
if (use_dynamic_memory_arrangement) {
|
2019-06-29 10:20:36 +01:00
|
|
|
/* 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. */
|
2020-03-29 23:24:40 +01:00
|
|
|
s64 total_memory = 0;
|
|
|
|
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(&total_memory, GetResourceLimitHandle(ResourceLimitGroup_System), svc::LimitableResource_PhysicalMemoryMax));
|
2019-06-29 10:20:36 +01:00
|
|
|
|
|
|
|
/* Get and save application + applet memory. */
|
2021-10-09 20:45:31 +01:00
|
|
|
R_ABORT_UNLESS(svc::GetSystemInfo(std::addressof(g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Application]), svc::SystemInfoType_TotalPhysicalMemorySize, svc::InvalidHandle, svc::PhysicalMemorySystemInfo_Application));
|
|
|
|
R_ABORT_UNLESS(svc::GetSystemInfo(std::addressof(g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Applet]), svc::SystemInfoType_TotalPhysicalMemorySize, svc::InvalidHandle, svc::PhysicalMemorySystemInfo_Applet));
|
2019-06-29 10:20:36 +01:00
|
|
|
|
2020-03-29 23:24:40 +01:00
|
|
|
const s64 application_size = g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Application];
|
|
|
|
const s64 applet_size = g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Applet];
|
|
|
|
const s64 reserved_non_system_size = (application_size + applet_size + ReservedMemorySize600);
|
2019-06-29 10:20:36 +01:00
|
|
|
|
|
|
|
/* Ensure there's enough memory for the system region. */
|
2020-02-23 07:05:14 +00:00
|
|
|
AMS_ABORT_UNLESS(reserved_non_system_size < total_memory);
|
2019-06-29 10:20:36 +01:00
|
|
|
|
|
|
|
g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_System] = total_memory - reserved_non_system_size;
|
|
|
|
} else {
|
2020-08-21 10:38:16 +01:00
|
|
|
/* Older system versions retrieve memory arrangement from spl, and use hardcoded profiles. */
|
2019-06-29 10:20:36 +01:00
|
|
|
g_memory_arrangement = spl::GetMemoryArrangement();
|
|
|
|
|
2020-08-21 10:38:16 +01:00
|
|
|
/* 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. */
|
2021-09-30 07:33:25 +01:00
|
|
|
const size_t extra_memory_size = ExtraSystemMemorySizeAtmosphere;
|
2020-04-14 06:19:44 +01:00
|
|
|
const auto src_group = hos_version >= hos::Version_3_0_0 ? ResourceLimitGroup_Applet : ResourceLimitGroup_Application;
|
2019-12-10 01:56:38 +00:00
|
|
|
for (size_t i = 0; i < spl::MemoryArrangement_Count; i++) {
|
|
|
|
g_memory_resource_limits[i][ResourceLimitGroup_System] += extra_memory_size;
|
|
|
|
g_memory_resource_limits[i][src_group] -= extra_memory_size;
|
2019-06-29 10:20:36 +01:00
|
|
|
}
|
2020-08-26 02:10:58 +01:00
|
|
|
|
|
|
|
/* If KTrace is enabled, account for that by subtracting the memory from the applet pool. */
|
|
|
|
if (IsKTraceEnabled()) {
|
|
|
|
constexpr size_t KTraceBufferSize = 16_MB;
|
|
|
|
for (size_t i = 0; i < spl::MemoryArrangement_Count; i++) {
|
|
|
|
g_memory_resource_limits[i][ResourceLimitGroup_Applet] -= KTraceBufferSize;
|
|
|
|
}
|
|
|
|
}
|
2019-06-29 10:20:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Actually set resource limits. */
|
|
|
|
{
|
2019-07-03 05:27:53 +01:00
|
|
|
std::scoped_lock lk(g_resource_limit_lock);
|
2019-06-29 10:20:36 +01:00
|
|
|
|
|
|
|
for (size_t group = 0; group < ResourceLimitGroup_Count; group++) {
|
2020-02-23 07:05:14 +00:00
|
|
|
R_ABORT_UNLESS(SetResourceLimitLimitValues(static_cast<ResourceLimitGroup>(group), g_memory_resource_limits[g_memory_arrangement][group]));
|
2019-06-29 10:20:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-22 06:52:16 +00:00
|
|
|
R_SUCCEED();
|
2019-06-29 10:20:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Result BoostSystemMemoryResourceLimit(u64 boost_size) {
|
2023-05-14 11:06:52 +01:00
|
|
|
/* Ensure only one boost change happens at a time. */
|
|
|
|
std::scoped_lock lk(g_system_memory_boost_lock);
|
2019-06-29 10:20:36 +01:00
|
|
|
|
2023-05-14 11:06:52 +01:00
|
|
|
/* Boost to the appropriate total amount. */
|
|
|
|
R_RETURN(BoostSystemMemoryResourceLimitLocked(boost_size, g_system_memory_boost_size_for_mitm));
|
|
|
|
}
|
2019-06-29 10:20:36 +01:00
|
|
|
|
2023-05-14 11:06:52 +01:00
|
|
|
Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size) {
|
|
|
|
/* Ensure only one boost change happens at a time. */
|
|
|
|
std::scoped_lock lk(g_system_memory_boost_lock);
|
2019-06-29 10:20:36 +01:00
|
|
|
|
2023-05-14 11:06:52 +01:00
|
|
|
/* Boost to the appropriate total amount. */
|
|
|
|
R_RETURN(BoostSystemMemoryResourceLimitLocked(g_system_memory_boost_size, boost_size));
|
2019-06-29 10:20:36 +01:00
|
|
|
}
|
|
|
|
|
2019-07-03 05:27:53 +01:00
|
|
|
Result BoostApplicationThreadResourceLimit() {
|
|
|
|
std::scoped_lock lk(g_resource_limit_lock);
|
2021-09-30 07:33:25 +01:00
|
|
|
|
2022-03-22 06:52:16 +00:00
|
|
|
/* Boost the limit. */
|
|
|
|
R_TRY(BoostThreadResourceLimitLocked(ResourceLimitGroup_Application));
|
|
|
|
|
|
|
|
R_SUCCEED();
|
|
|
|
}
|
|
|
|
|
|
|
|
Result BoostSystemThreadResourceLimit() {
|
|
|
|
std::scoped_lock lk(g_resource_limit_lock);
|
2019-07-03 05:27:53 +01:00
|
|
|
|
2022-03-22 06:52:16 +00:00
|
|
|
/* Boost the limits. */
|
|
|
|
R_TRY(BoostThreadResourceLimitLocked(ResourceLimitGroup_Applet));
|
|
|
|
R_TRY(BoostThreadResourceLimitLocked(ResourceLimitGroup_System));
|
2019-07-03 05:27:53 +01:00
|
|
|
|
2022-03-22 06:52:16 +00:00
|
|
|
R_SUCCEED();
|
2019-06-29 10:20:36 +01:00
|
|
|
}
|
|
|
|
|
2021-10-05 01:12:32 +01:00
|
|
|
os::NativeHandle GetResourceLimitHandle(ResourceLimitGroup group) {
|
2019-06-29 10:20:36 +01:00
|
|
|
return g_resource_limit_handles[group];
|
|
|
|
}
|
|
|
|
|
2021-10-05 01:12:32 +01:00
|
|
|
os::NativeHandle GetResourceLimitHandle(const ldr::ProgramInfo *info) {
|
2019-06-29 10:20:36 +01:00
|
|
|
return GetResourceLimitHandle(GetResourceLimitGroup(info));
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaitResourceAvailable(const ldr::ProgramInfo *info) {
|
|
|
|
if (GetResourceLimitGroup(info) == ResourceLimitGroup_Application) {
|
|
|
|
WaitResourceAvailable(ResourceLimitGroup_Application);
|
2020-04-14 06:19:44 +01:00
|
|
|
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
2019-06-29 10:20:36 +01:00
|
|
|
WaitApplicationMemoryAvailable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-26 22:44:32 +01:00
|
|
|
Result GetResourceLimitCurrentValue(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
|
|
|
|
R_RETURN(GetResourceLimitValueImpl<::ams::svc::GetResourceLimitCurrentValue>(out, group));
|
2022-03-22 06:52:16 +00:00
|
|
|
}
|
|
|
|
|
2023-10-26 22:44:32 +01:00
|
|
|
Result GetResourceLimitPeakValue(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
|
|
|
|
R_RETURN(GetResourceLimitValueImpl<::ams::svc::GetResourceLimitPeakValue>(out, group));
|
2022-03-22 06:52:16 +00:00
|
|
|
}
|
|
|
|
|
2023-10-26 22:44:32 +01:00
|
|
|
Result GetResourceLimitLimitValue(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
|
|
|
|
R_RETURN(GetResourceLimitValueImpl<::ams::svc::GetResourceLimitLimitValue>(out, group));
|
2022-03-22 06:52:16 +00:00
|
|
|
}
|
|
|
|
|
2020-03-29 23:24:40 +01:00
|
|
|
Result GetResourceLimitValues(s64 *out_cur, s64 *out_lim, ResourceLimitGroup group, svc::LimitableResource resource) {
|
2019-06-29 10:20:36 +01:00
|
|
|
/* Do not allow out of bounds access. */
|
2020-02-23 07:05:14 +00:00
|
|
|
AMS_ABORT_UNLESS(group < ResourceLimitGroup_Count);
|
2020-03-29 23:24:40 +01:00
|
|
|
AMS_ABORT_UNLESS(resource < svc::LimitableResource_Count);
|
2019-06-29 10:20:36 +01:00
|
|
|
|
2021-10-05 01:12:32 +01:00
|
|
|
const auto reslimit_hnd = GetResourceLimitHandle(group);
|
2020-03-29 23:24:40 +01:00
|
|
|
R_TRY(svc::GetResourceLimitCurrentValue(out_cur, reslimit_hnd, resource));
|
|
|
|
R_TRY(svc::GetResourceLimitLimitValue(out_lim, reslimit_hnd, resource));
|
2019-06-29 10:20:36 +01:00
|
|
|
|
2022-03-22 06:52:16 +00:00
|
|
|
R_SUCCEED();
|
2019-06-29 10:20:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|