kernel: Improve accuracy of KResourceLimit emulation (#7221)

* core: Refactor resource limits

* svc: Implement SetResourceLimitLimitValues

* Also correct existing name and add missing error codes
This commit is contained in:
GPUCode 2023-12-04 13:31:06 +02:00 committed by GitHub
parent 875f5eaad5
commit 59df319f48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 421 additions and 301 deletions

View file

@ -9,6 +9,7 @@
#include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
#include "core/memory.h" #include "core/memory.h"
@ -68,13 +69,16 @@ bool AddressArbiter::ResumeHighestPriorityThread(VAddr address) {
AddressArbiter::AddressArbiter(KernelSystem& kernel) AddressArbiter::AddressArbiter(KernelSystem& kernel)
: Object(kernel), kernel(kernel), timeout_callback(std::make_shared<Callback>(*this)) {} : Object(kernel), kernel(kernel), timeout_callback(std::make_shared<Callback>(*this)) {}
AddressArbiter::~AddressArbiter() {}
AddressArbiter::~AddressArbiter() {
if (resource_limit) {
resource_limit->Release(ResourceLimitType::AddressArbiter, 1);
}
}
std::shared_ptr<AddressArbiter> KernelSystem::CreateAddressArbiter(std::string name) { std::shared_ptr<AddressArbiter> KernelSystem::CreateAddressArbiter(std::string name) {
auto address_arbiter{std::make_shared<AddressArbiter>(*this)}; auto address_arbiter = std::make_shared<AddressArbiter>(*this);
address_arbiter->name = std::move(name); address_arbiter->name = std::move(name);
return address_arbiter; return address_arbiter;
} }

View file

@ -25,6 +25,7 @@
namespace Kernel { namespace Kernel {
class Thread; class Thread;
class ResourceLimit;
enum class ArbitrationType : u32 { enum class ArbitrationType : u32 {
Signal, Signal,
@ -51,6 +52,7 @@ public:
return HANDLE_TYPE; return HANDLE_TYPE;
} }
std::shared_ptr<ResourceLimit> resource_limit;
std::string name; ///< Name of address arbiter object (optional) std::string name; ///< Name of address arbiter object (optional)
ResultCode ArbitrateAddress(std::shared_ptr<Thread> thread, ArbitrationType type, VAddr address, ResultCode ArbitrateAddress(std::shared_ptr<Thread> thread, ArbitrationType type, VAddr address,
@ -86,6 +88,7 @@ private:
ar& name; ar& name;
ar& waiting_threads; ar& waiting_threads;
ar& timeout_callback; ar& timeout_callback;
ar& resource_limit;
} }
}; };

View file

@ -9,6 +9,12 @@
namespace Kernel { namespace Kernel {
namespace ErrCodes { namespace ErrCodes {
enum { enum {
OutOfSharedMems = 11,
OutOfThreads = 12,
OutOfMutexes = 13,
OutOfSemaphores = 14,
OutOfEvents = 15,
OutOfTimers = 16,
OutOfHandles = 19, OutOfHandles = 19,
SessionClosedByRemote = 26, SessionClosedByRemote = 26,
PortNameTooLong = 30, PortNameTooLong = 30,
@ -16,6 +22,7 @@ enum {
NoPendingSessions = 35, NoPendingSessions = 35,
WrongPermission = 46, WrongPermission = 46,
InvalidBufferDescriptor = 48, InvalidBufferDescriptor = 48,
OutOfAddressArbiters = 51,
MaxConnectionsReached = 52, MaxConnectionsReached = 52,
CommandTooLarge = 54, CommandTooLarge = 54,
}; };

View file

@ -2,12 +2,11 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <algorithm>
#include <vector>
#include "common/archives.h" #include "common/archives.h"
#include "common/assert.h" #include "common/assert.h"
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
SERIALIZE_EXPORT_IMPL(Kernel::Event) SERIALIZE_EXPORT_IMPL(Kernel::Event)
@ -15,16 +14,19 @@ SERIALIZE_EXPORT_IMPL(Kernel::Event)
namespace Kernel { namespace Kernel {
Event::Event(KernelSystem& kernel) : WaitObject(kernel) {} Event::Event(KernelSystem& kernel) : WaitObject(kernel) {}
Event::~Event() {}
Event::~Event() {
if (resource_limit) {
resource_limit->Release(ResourceLimitType::Event, 1);
}
}
std::shared_ptr<Event> KernelSystem::CreateEvent(ResetType reset_type, std::string name) { std::shared_ptr<Event> KernelSystem::CreateEvent(ResetType reset_type, std::string name) {
auto evt{std::make_shared<Event>(*this)}; auto event = std::make_shared<Event>(*this);
event->signaled = false;
evt->signaled = false; event->reset_type = reset_type;
evt->reset_type = reset_type; event->name = std::move(name);
evt->name = std::move(name); return event;
return evt;
} }
bool Event::ShouldWait(const Thread* thread) const { bool Event::ShouldWait(const Thread* thread) const {

View file

@ -7,8 +7,8 @@
#include <boost/serialization/base_object.hpp> #include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp> #include <boost/serialization/export.hpp>
#include <boost/serialization/string.hpp> #include <boost/serialization/string.hpp>
#include "common/common_types.h"
#include "core/hle/kernel/object.h" #include "core/hle/kernel/object.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/wait_object.h" #include "core/hle/kernel/wait_object.h"
namespace Kernel { namespace Kernel {
@ -45,6 +45,8 @@ public:
void Signal(); void Signal();
void Clear(); void Clear();
std::shared_ptr<ResourceLimit> resource_limit;
private: private:
ResetType reset_type; ///< Current ResetType ResetType reset_type; ///< Current ResetType
@ -60,6 +62,7 @@ private:
ar& reset_type; ar& reset_type;
ar& signaled; ar& signaled;
ar& name; ar& name;
ar& resource_limit;
} }
}; };

View file

@ -137,6 +137,10 @@ const SharedPage::Handler& KernelSystem::GetSharedPageHandler() const {
return *shared_page_handler; return *shared_page_handler;
} }
ConfigMem::Handler& KernelSystem::GetConfigMemHandler() {
return *config_mem_handler;
}
IPCDebugger::Recorder& KernelSystem::GetIPCRecorder() { IPCDebugger::Recorder& KernelSystem::GetIPCRecorder() {
return *ipc_recorder; return *ipc_recorder;
} }

View file

@ -295,6 +295,8 @@ public:
SharedPage::Handler& GetSharedPageHandler(); SharedPage::Handler& GetSharedPageHandler();
const SharedPage::Handler& GetSharedPageHandler() const; const SharedPage::Handler& GetSharedPageHandler() const;
ConfigMem::Handler& GetConfigMemHandler();
IPCDebugger::Recorder& GetIPCRecorder(); IPCDebugger::Recorder& GetIPCRecorder();
const IPCDebugger::Recorder& GetIPCRecorder() const; const IPCDebugger::Recorder& GetIPCRecorder() const;

View file

@ -2,15 +2,14 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <vector>
#include "common/archives.h" #include "common/archives.h"
#include "common/assert.h" #include "common/assert.h"
#include "core/core.h" #include "core/core.h"
#include "core/global.h"
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/object.h" #include "core/hle/kernel/object.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
SERIALIZE_EXPORT_IMPL(Kernel::Mutex) SERIALIZE_EXPORT_IMPL(Kernel::Mutex)
@ -27,18 +26,23 @@ void ReleaseThreadMutexes(Thread* thread) {
} }
Mutex::Mutex(KernelSystem& kernel) : WaitObject(kernel), kernel(kernel) {} Mutex::Mutex(KernelSystem& kernel) : WaitObject(kernel), kernel(kernel) {}
Mutex::~Mutex() {}
Mutex::~Mutex() {
if (resource_limit) {
resource_limit->Release(ResourceLimitType::Mutex, 1);
}
}
std::shared_ptr<Mutex> KernelSystem::CreateMutex(bool initial_locked, std::string name) { std::shared_ptr<Mutex> KernelSystem::CreateMutex(bool initial_locked, std::string name) {
auto mutex{std::make_shared<Mutex>(*this)}; auto mutex = std::make_shared<Mutex>(*this);
mutex->lock_count = 0; mutex->lock_count = 0;
mutex->name = std::move(name); mutex->name = std::move(name);
mutex->holding_thread = nullptr; mutex->holding_thread = nullptr;
// Acquire mutex with current thread if initialized as locked // Acquire mutex with current thread if initialized as locked
if (initial_locked) if (initial_locked) {
mutex->Acquire(thread_managers[current_cpu->GetID()]->GetCurrentThread()); mutex->Acquire(GetCurrentThreadManager().GetCurrentThread());
}
return mutex; return mutex;
} }

View file

@ -12,6 +12,7 @@
#include <boost/serialization/string.hpp> #include <boost/serialization/string.hpp>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/wait_object.h" #include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h" #include "core/hle/result.h"
@ -36,6 +37,7 @@ public:
return HANDLE_TYPE; return HANDLE_TYPE;
} }
std::shared_ptr<ResourceLimit> resource_limit;
int lock_count; ///< Number of times the mutex has been acquired int lock_count; ///< Number of times the mutex has been acquired
u32 priority; ///< The priority of the mutex, used for priority inheritance. u32 priority; ///< The priority of the mutex, used for priority inheritance.
std::string name; ///< Name of mutex (optional) std::string name; ///< Name of mutex (optional)
@ -71,6 +73,7 @@ private:
ar& priority; ar& priority;
ar& name; ar& name;
ar& holding_thread; ar& holding_thread;
ar& resource_limit;
} }
}; };

View file

@ -185,6 +185,11 @@ void Process::Set3dsxKernelCaps() {
void Process::Run(s32 main_thread_priority, u32 stack_size) { void Process::Run(s32 main_thread_priority, u32 stack_size) {
memory_region = kernel.GetMemoryRegion(flags.memory_region); memory_region = kernel.GetMemoryRegion(flags.memory_region);
// Ensure we can reserve a thread. Real kernel returns 0xC860180C if this fails.
if (!resource_limit->Reserve(ResourceLimitType::Thread, 1)) {
return;
}
auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) { MemoryState memory_state) {
HeapAllocate(segment.addr, segment.size, permissions, memory_state, true); HeapAllocate(segment.addr, segment.size, permissions, memory_state, true);
@ -281,7 +286,7 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u32 size, VMAPermission per
holding_memory += allocated_fcram; holding_memory += allocated_fcram;
memory_used += size; memory_used += size;
resource_limit->current_commit += size; resource_limit->Reserve(ResourceLimitType::Commit, size);
return target; return target;
} }
@ -310,7 +315,7 @@ ResultCode Process::HeapFree(VAddr target, u32 size) {
ASSERT(result.IsSuccess()); ASSERT(result.IsSuccess());
memory_used -= size; memory_used -= size;
resource_limit->current_commit -= size; resource_limit->Release(ResourceLimitType::Commit, size);
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -356,7 +361,7 @@ ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission p
holding_memory += MemoryRegionInfo::Interval(physical_offset, physical_offset + size); holding_memory += MemoryRegionInfo::Interval(physical_offset, physical_offset + size);
memory_used += size; memory_used += size;
resource_limit->current_commit += size; resource_limit->Reserve(ResourceLimitType::Commit, size);
LOG_DEBUG(Kernel, "Allocated at target={:08X}", target); LOG_DEBUG(Kernel, "Allocated at target={:08X}", target);
return target; return target;
@ -385,7 +390,7 @@ ResultCode Process::LinearFree(VAddr target, u32 size) {
holding_memory -= MemoryRegionInfo::Interval(physical_offset, physical_offset + size); holding_memory -= MemoryRegionInfo::Interval(physical_offset, physical_offset + size);
memory_used -= size; memory_used -= size;
resource_limit->current_commit -= size; resource_limit->Release(ResourceLimitType::Commit, size);
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -569,7 +574,7 @@ void Process::FreeAllMemory() {
auto size = entry.upper() - entry.lower(); auto size = entry.upper() - entry.lower();
memory_region->Free(entry.lower(), size); memory_region->Free(entry.lower(), size);
memory_used -= size; memory_used -= size;
resource_limit->current_commit -= size; resource_limit->Release(ResourceLimitType::Commit, size);
} }
holding_memory.clear(); holding_memory.clear();
@ -590,7 +595,7 @@ void Process::FreeAllMemory() {
// leaks in these values still. // leaks in these values still.
LOG_DEBUG(Kernel, "Remaining memory used after process cleanup: 0x{:08X}", memory_used); LOG_DEBUG(Kernel, "Remaining memory used after process cleanup: 0x{:08X}", memory_used);
LOG_DEBUG(Kernel, "Remaining memory resource commit after process cleanup: 0x{:08X}", LOG_DEBUG(Kernel, "Remaining memory resource commit after process cleanup: 0x{:08X}",
resource_limit->current_commit); resource_limit->GetCurrentValue(ResourceLimitType::Commit));
} }
Kernel::Process::Process(KernelSystem& kernel) Kernel::Process::Process(KernelSystem& kernel)

View file

@ -2,10 +2,8 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <cstring>
#include "common/archives.h" #include "common/archives.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/resource_limit.h"
@ -14,145 +12,130 @@ SERIALIZE_EXPORT_IMPL(Kernel::ResourceLimit)
namespace Kernel { namespace Kernel {
ResourceLimit::ResourceLimit(KernelSystem& kernel) : Object(kernel) {} ResourceLimit::ResourceLimit(KernelSystem& kernel) : Object(kernel) {}
ResourceLimit::~ResourceLimit() {}
ResourceLimit::~ResourceLimit() = default;
std::shared_ptr<ResourceLimit> ResourceLimit::Create(KernelSystem& kernel, std::string name) { std::shared_ptr<ResourceLimit> ResourceLimit::Create(KernelSystem& kernel, std::string name) {
auto resource_limit{std::make_shared<ResourceLimit>(kernel)}; auto resource_limit = std::make_shared<ResourceLimit>(kernel);
resource_limit->m_name = std::move(name);
resource_limit->name = std::move(name);
return resource_limit; return resource_limit;
} }
std::shared_ptr<ResourceLimit> ResourceLimitList::GetForCategory(ResourceLimitCategory category) { s32 ResourceLimit::GetCurrentValue(ResourceLimitType type) const {
switch (category) { const auto index = static_cast<size_t>(type);
case ResourceLimitCategory::APPLICATION: return m_current_values[index];
case ResourceLimitCategory::SYS_APPLET:
case ResourceLimitCategory::LIB_APPLET:
case ResourceLimitCategory::OTHER:
return resource_limits[static_cast<u8>(category)];
default:
LOG_CRITICAL(Kernel, "Unknown resource limit category");
UNREACHABLE();
}
} }
s32 ResourceLimit::GetCurrentResourceValue(u32 resource) const { s32 ResourceLimit::GetLimitValue(ResourceLimitType type) const {
switch (resource) { const auto index = static_cast<size_t>(type);
case COMMIT: return m_limit_values[index];
return current_commit;
case THREAD:
return current_threads;
case EVENT:
return current_events;
case MUTEX:
return current_mutexes;
case SEMAPHORE:
return current_semaphores;
case TIMER:
return current_timers;
case SHARED_MEMORY:
return current_shared_mems;
case ADDRESS_ARBITER:
return current_address_arbiters;
case CPU_TIME:
return current_cpu_time;
default:
LOG_ERROR(Kernel, "Unknown resource type={:08X}", resource);
UNIMPLEMENTED();
return 0;
}
} }
u32 ResourceLimit::GetMaxResourceValue(u32 resource) const { void ResourceLimit::SetLimitValue(ResourceLimitType type, s32 value) {
switch (resource) { const auto index = static_cast<size_t>(type);
case PRIORITY: m_limit_values[index] = value;
return max_priority;
case COMMIT:
return max_commit;
case THREAD:
return max_threads;
case EVENT:
return max_events;
case MUTEX:
return max_mutexes;
case SEMAPHORE:
return max_semaphores;
case TIMER:
return max_timers;
case SHARED_MEMORY:
return max_shared_mems;
case ADDRESS_ARBITER:
return max_address_arbiters;
case CPU_TIME:
return max_cpu_time;
default:
LOG_ERROR(Kernel, "Unknown resource type={:08X}", resource);
UNIMPLEMENTED();
return 0;
} }
bool ResourceLimit::Reserve(ResourceLimitType type, s32 amount) {
const auto index = static_cast<size_t>(type);
const s32 limit = m_limit_values[index];
const s32 new_value = m_current_values[index] + amount;
if (new_value > limit) {
LOG_ERROR(Kernel, "New value {} exceeds limit {} for resource type {}", new_value, limit,
type);
return false;
}
m_current_values[index] = new_value;
return true;
}
bool ResourceLimit::Release(ResourceLimitType type, s32 amount) {
const auto index = static_cast<size_t>(type);
const s32 value = m_current_values[index];
if (amount > value) {
LOG_ERROR(Kernel, "Amount {} exceeds current value {} for resource type {}", amount, value,
type);
return false;
}
m_current_values[index] = value - amount;
return true;
} }
ResourceLimitList::ResourceLimitList(KernelSystem& kernel) { ResourceLimitList::ResourceLimitList(KernelSystem& kernel) {
// Create the four resource limits that the system uses // PM makes APPMEMALLOC always match app RESLIMIT_COMMIT.
// Create the APPLICATION resource limit // See: https://github.com/LumaTeam/Luma3DS/blob/e2778a45/sysmodules/pm/source/reslimit.c#L275
const bool is_new_3ds = Settings::values.is_new_3ds.GetValue(); const bool is_new_3ds = Settings::values.is_new_3ds.GetValue();
const auto& appmemalloc = kernel.GetMemoryRegion(MemoryRegion::APPLICATION);
std::shared_ptr<ResourceLimit> resource_limit = ResourceLimit::Create(kernel, "Applications"); // Create the Application resource limit
resource_limit->max_priority = 0x18; auto resource_limit = ResourceLimit::Create(kernel, "Applications");
resource_limit->max_commit = is_new_3ds ? 0x7C00000 : 0x4000000; resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x18);
resource_limit->max_threads = 0x20; resource_limit->SetLimitValue(ResourceLimitType::Commit, appmemalloc->size);
resource_limit->max_events = 0x20; resource_limit->SetLimitValue(ResourceLimitType::Thread, 0x20);
resource_limit->max_mutexes = 0x20; resource_limit->SetLimitValue(ResourceLimitType::Event, 0x20);
resource_limit->max_semaphores = 0x8; resource_limit->SetLimitValue(ResourceLimitType::Mutex, 0x20);
resource_limit->max_timers = 0x8; resource_limit->SetLimitValue(ResourceLimitType::Semaphore, 0x8);
resource_limit->max_shared_mems = 0x10; resource_limit->SetLimitValue(ResourceLimitType::Timer, 0x8);
resource_limit->max_address_arbiters = 0x2; resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, 0x10);
resource_limit->max_cpu_time = 0x0; resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, 0x2);
resource_limits[static_cast<u8>(ResourceLimitCategory::APPLICATION)] = resource_limit; resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x0);
resource_limits[static_cast<u8>(ResourceLimitCategory::Application)] = resource_limit;
// Create the SYS_APPLET resource limit // Create the SysApplet resource limit
resource_limit = ResourceLimit::Create(kernel, "System Applets"); resource_limit = ResourceLimit::Create(kernel, "System Applets");
resource_limit->max_priority = 0x4; resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x4);
resource_limit->max_commit = is_new_3ds ? 0x5E06000 : 0x2606000; resource_limit->SetLimitValue(ResourceLimitType::Commit, is_new_3ds ? 0x5E06000 : 0x2606000);
resource_limit->max_threads = is_new_3ds ? 0x1D : 0xE; resource_limit->SetLimitValue(ResourceLimitType::Thread, is_new_3ds ? 0x1D : 0xE);
resource_limit->max_events = is_new_3ds ? 0xB : 0x8; resource_limit->SetLimitValue(ResourceLimitType::Event, is_new_3ds ? 0xB : 0x8);
resource_limit->max_mutexes = 0x8; resource_limit->SetLimitValue(ResourceLimitType::Mutex, 0x8);
resource_limit->max_semaphores = 0x4; resource_limit->SetLimitValue(ResourceLimitType::Semaphore, 0x4);
resource_limit->max_timers = 0x4; resource_limit->SetLimitValue(ResourceLimitType::Timer, 0x4);
resource_limit->max_shared_mems = 0x8; resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, 0x8);
resource_limit->max_address_arbiters = 0x3; resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, 0x3);
resource_limit->max_cpu_time = 0x2710; resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x2710);
resource_limits[static_cast<u8>(ResourceLimitCategory::SYS_APPLET)] = resource_limit; resource_limits[static_cast<u8>(ResourceLimitCategory::SysApplet)] = resource_limit;
// Create the LIB_APPLET resource limit // Create the LibApplet resource limit
resource_limit = ResourceLimit::Create(kernel, "Library Applets"); resource_limit = ResourceLimit::Create(kernel, "Library Applets");
resource_limit->max_priority = 0x4; resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x4);
resource_limit->max_commit = 0x602000; resource_limit->SetLimitValue(ResourceLimitType::Commit, 0x602000);
resource_limit->max_threads = 0xE; resource_limit->SetLimitValue(ResourceLimitType::Thread, 0xE);
resource_limit->max_events = 0x8; resource_limit->SetLimitValue(ResourceLimitType::Event, 0x8);
resource_limit->max_mutexes = 0x8; resource_limit->SetLimitValue(ResourceLimitType::Mutex, 0x8);
resource_limit->max_semaphores = 0x4; resource_limit->SetLimitValue(ResourceLimitType::Semaphore, 0x4);
resource_limit->max_timers = 0x4; resource_limit->SetLimitValue(ResourceLimitType::Timer, 0x4);
resource_limit->max_shared_mems = 0x8; resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, 0x8);
resource_limit->max_address_arbiters = 0x1; resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, 0x1);
resource_limit->max_cpu_time = 0x2710; resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x2710);
resource_limits[static_cast<u8>(ResourceLimitCategory::LIB_APPLET)] = resource_limit; resource_limits[static_cast<u8>(ResourceLimitCategory::LibApplet)] = resource_limit;
// Create the OTHER resource limit // Create the Other resource limit
resource_limit = ResourceLimit::Create(kernel, "Others"); resource_limit = ResourceLimit::Create(kernel, "Others");
resource_limit->max_priority = 0x4; resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x4);
resource_limit->max_commit = is_new_3ds ? 0x2182000 : 0x1682000; resource_limit->SetLimitValue(ResourceLimitType::Commit, is_new_3ds ? 0x2182000 : 0x1682000);
resource_limit->max_threads = is_new_3ds ? 0xE1 : 0xCA; resource_limit->SetLimitValue(ResourceLimitType::Thread, is_new_3ds ? 0xE1 : 0xCA);
resource_limit->max_events = is_new_3ds ? 0x108 : 0xF8; resource_limit->SetLimitValue(ResourceLimitType::Event, is_new_3ds ? 0x108 : 0xF8);
resource_limit->max_mutexes = is_new_3ds ? 0x25 : 0x23; resource_limit->SetLimitValue(ResourceLimitType::Mutex, is_new_3ds ? 0x25 : 0x23);
resource_limit->max_semaphores = is_new_3ds ? 0x43 : 0x40; resource_limit->SetLimitValue(ResourceLimitType::Semaphore, is_new_3ds ? 0x43 : 0x40);
resource_limit->max_timers = is_new_3ds ? 0x2C : 0x2B; resource_limit->SetLimitValue(ResourceLimitType::Timer, is_new_3ds ? 0x2C : 0x2B);
resource_limit->max_shared_mems = is_new_3ds ? 0x1F : 0x1E; resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, is_new_3ds ? 0x1F : 0x1E);
resource_limit->max_address_arbiters = is_new_3ds ? 0x2D : 0x2B; resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, is_new_3ds ? 0x2D : 0x2B);
resource_limit->max_cpu_time = 0x3E8; resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x3E8);
resource_limits[static_cast<u8>(ResourceLimitCategory::OTHER)] = resource_limit; resource_limits[static_cast<u8>(ResourceLimitCategory::Other)] = resource_limit;
} }
ResourceLimitList::~ResourceLimitList() = default; ResourceLimitList::~ResourceLimitList() = default;
std::shared_ptr<ResourceLimit> ResourceLimitList::GetForCategory(ResourceLimitCategory category) {
switch (category) {
case ResourceLimitCategory::Application:
case ResourceLimitCategory::SysApplet:
case ResourceLimitCategory::LibApplet:
case ResourceLimitCategory::Other:
return resource_limits[static_cast<u8>(category)];
default:
UNREACHABLE_MSG("Unknown resource limit category");
}
}
} // namespace Kernel } // namespace Kernel

View file

@ -16,23 +16,24 @@
namespace Kernel { namespace Kernel {
enum class ResourceLimitCategory : u8 { enum class ResourceLimitCategory : u8 {
APPLICATION = 0, Application = 0,
SYS_APPLET = 1, SysApplet = 1,
LIB_APPLET = 2, LibApplet = 2,
OTHER = 3 Other = 3,
}; };
enum ResourceTypes { enum class ResourceLimitType : u32 {
PRIORITY = 0, Priority = 0,
COMMIT = 1, Commit = 1,
THREAD = 2, Thread = 2,
EVENT = 3, Event = 3,
MUTEX = 4, Mutex = 4,
SEMAPHORE = 5, Semaphore = 5,
TIMER = 6, Timer = 6,
SHARED_MEMORY = 7, SharedMemory = 7,
ADDRESS_ARBITER = 8, AddressArbiter = 8,
CPU_TIME = 9, CpuTime = 9,
Max = 10,
}; };
class ResourceLimit final : public Object { class ResourceLimit final : public Object {
@ -50,7 +51,7 @@ public:
return "ResourceLimit"; return "ResourceLimit";
} }
std::string GetName() const override { std::string GetName() const override {
return name; return m_name;
} }
static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit; static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit;
@ -58,90 +59,28 @@ public:
return HANDLE_TYPE; return HANDLE_TYPE;
} }
/** s32 GetCurrentValue(ResourceLimitType type) const;
* Gets the current value for the specified resource. s32 GetLimitValue(ResourceLimitType type) const;
* @param resource Requested resource type
* @returns The current value of the resource type
*/
s32 GetCurrentResourceValue(u32 resource) const;
/** void SetLimitValue(ResourceLimitType name, s32 value);
* Gets the max value for the specified resource.
* @param resource Requested resource type
* @returns The max value of the resource type
*/
u32 GetMaxResourceValue(u32 resource) const;
/// Name of resource limit object. bool Reserve(ResourceLimitType type, s32 amount);
std::string name; bool Release(ResourceLimitType type, s32 amount);
/// Max thread priority that a process in this category can create private:
s32 max_priority = 0; using ResourceArray = std::array<s32, static_cast<size_t>(ResourceLimitType::Max)>;
ResourceArray m_limit_values{};
/// Max memory that processes in this category can use ResourceArray m_current_values{};
s32 max_commit = 0; std::string m_name;
///< Max number of objects that can be collectively created by the processes in this category
s32 max_threads = 0;
s32 max_events = 0;
s32 max_mutexes = 0;
s32 max_semaphores = 0;
s32 max_timers = 0;
s32 max_shared_mems = 0;
s32 max_address_arbiters = 0;
/// Max CPU time that the processes in this category can utilize
s32 max_cpu_time = 0;
// TODO(Subv): Increment these in their respective Kernel::T::Create functions, keeping in mind
// that APPLICATION resource limits should not be affected by the objects created by service
// modules.
// Currently we have no way of distinguishing if a Create was called by the running application,
// or by a service module. Approach this once we have separated the service modules into their
// own processes
/// Current memory that the processes in this category are using
s32 current_commit = 0;
///< Current number of objects among all processes in this category
s32 current_threads = 0;
s32 current_events = 0;
s32 current_mutexes = 0;
s32 current_semaphores = 0;
s32 current_timers = 0;
s32 current_shared_mems = 0;
s32 current_address_arbiters = 0;
/// Current CPU time that the processes in this category are utilizing
s32 current_cpu_time = 0;
private: private:
friend class boost::serialization::access; friend class boost::serialization::access;
template <class Archive> template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) { void serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<Object>(*this); ar& boost::serialization::base_object<Object>(*this);
// NB most of these aren't used at all currently, but we're adding them here for forwards ar& m_name;
// compatibility ar& m_limit_values;
ar& name; ar& m_current_values;
ar& max_priority;
ar& max_commit;
ar& max_threads;
ar& max_events;
ar& max_mutexes;
ar& max_semaphores;
ar& max_timers;
ar& max_shared_mems;
ar& max_address_arbiters;
ar& max_cpu_time;
ar& current_commit;
ar& current_threads;
ar& current_events;
ar& current_mutexes;
ar& current_semaphores;
ar& current_timers;
ar& current_shared_mems;
ar& current_address_arbiters;
ar& current_cpu_time;
} }
}; };

View file

@ -3,9 +3,10 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "common/archives.h" #include "common/archives.h"
#include "common/assert.h"
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/semaphore.h" #include "core/hle/kernel/semaphore.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
@ -14,23 +15,27 @@ SERIALIZE_EXPORT_IMPL(Kernel::Semaphore)
namespace Kernel { namespace Kernel {
Semaphore::Semaphore(KernelSystem& kernel) : WaitObject(kernel) {} Semaphore::Semaphore(KernelSystem& kernel) : WaitObject(kernel) {}
Semaphore::~Semaphore() {}
Semaphore::~Semaphore() {
if (resource_limit) {
resource_limit->Release(ResourceLimitType::Semaphore, 1);
}
}
ResultVal<std::shared_ptr<Semaphore>> KernelSystem::CreateSemaphore(s32 initial_count, ResultVal<std::shared_ptr<Semaphore>> KernelSystem::CreateSemaphore(s32 initial_count,
s32 max_count, s32 max_count,
std::string name) { std::string name) {
if (initial_count > max_count) if (initial_count > max_count) {
return ERR_INVALID_COMBINATION_KERNEL; return ERR_INVALID_COMBINATION_KERNEL;
}
auto semaphore{std::make_shared<Semaphore>(*this)};
// When the semaphore is created, some slots are reserved for other threads, // When the semaphore is created, some slots are reserved for other threads,
// and the rest is reserved for the caller thread // and the rest is reserved for the caller thread
auto semaphore = std::make_shared<Semaphore>(*this);
semaphore->max_count = max_count; semaphore->max_count = max_count;
semaphore->available_count = initial_count; semaphore->available_count = initial_count;
semaphore->name = std::move(name); semaphore->name = std::move(name);
return semaphore; return semaphore;
} }

View file

@ -8,7 +8,6 @@
#include <boost/serialization/base_object.hpp> #include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp> #include <boost/serialization/export.hpp>
#include <boost/serialization/string.hpp> #include <boost/serialization/string.hpp>
#include <queue>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/kernel/object.h" #include "core/hle/kernel/object.h"
#include "core/hle/kernel/wait_object.h" #include "core/hle/kernel/wait_object.h"
@ -16,6 +15,8 @@
namespace Kernel { namespace Kernel {
class ResourceLimit;
class Semaphore final : public WaitObject { class Semaphore final : public WaitObject {
public: public:
explicit Semaphore(KernelSystem& kernel); explicit Semaphore(KernelSystem& kernel);
@ -33,6 +34,7 @@ public:
return HANDLE_TYPE; return HANDLE_TYPE;
} }
std::shared_ptr<ResourceLimit> resource_limit;
s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have
s32 available_count; ///< Number of free slots left in the semaphore s32 available_count; ///< Number of free slots left in the semaphore
std::string name; ///< Name of semaphore (optional) std::string name; ///< Name of semaphore (optional)
@ -55,6 +57,7 @@ private:
ar& max_count; ar& max_count;
ar& available_count; ar& available_count;
ar& name; ar& name;
ar& resource_limit;
} }
}; };

View file

@ -6,6 +6,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory.h" #include "core/hle/kernel/memory.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/shared_memory.h"
#include "core/memory.h" #include "core/memory.h"
@ -14,6 +15,7 @@ SERIALIZE_EXPORT_IMPL(Kernel::SharedMemory)
namespace Kernel { namespace Kernel {
SharedMemory::SharedMemory(KernelSystem& kernel) : Object(kernel), kernel(kernel) {} SharedMemory::SharedMemory(KernelSystem& kernel) : Object(kernel), kernel(kernel) {}
SharedMemory::~SharedMemory() { SharedMemory::~SharedMemory() {
for (const auto& interval : holding_memory) { for (const auto& interval : holding_memory) {
kernel.GetMemoryRegion(MemoryRegion::SYSTEM) kernel.GetMemoryRegion(MemoryRegion::SYSTEM)
@ -22,6 +24,7 @@ SharedMemory::~SharedMemory() {
auto process = owner_process.lock(); auto process = owner_process.lock();
if (process) { if (process) {
process->resource_limit->Release(ResourceLimitType::SharedMemory, 1);
if (base_address != 0) { if (base_address != 0) {
process->vm_manager.ChangeMemoryState(base_address, size, MemoryState::Locked, process->vm_manager.ChangeMemoryState(base_address, size, MemoryState::Locked,
VMAPermission::None, MemoryState::Private, VMAPermission::None, MemoryState::Private,
@ -35,8 +38,8 @@ SharedMemory::~SharedMemory() {
ResultVal<std::shared_ptr<SharedMemory>> KernelSystem::CreateSharedMemory( ResultVal<std::shared_ptr<SharedMemory>> KernelSystem::CreateSharedMemory(
std::shared_ptr<Process> owner_process, u32 size, MemoryPermission permissions, std::shared_ptr<Process> owner_process, u32 size, MemoryPermission permissions,
MemoryPermission other_permissions, VAddr address, MemoryRegion region, std::string name) { MemoryPermission other_permissions, VAddr address, MemoryRegion region, std::string name) {
auto shared_memory{std::make_shared<SharedMemory>(*this)};
auto shared_memory = std::make_shared<SharedMemory>(*this);
shared_memory->owner_process = owner_process; shared_memory->owner_process = owner_process;
shared_memory->name = std::move(name); shared_memory->name = std::move(name);
shared_memory->size = size; shared_memory->size = size;

View file

@ -16,6 +16,7 @@
#include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h" #include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/config_mem.h"
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
@ -390,6 +391,8 @@ private:
VAddr names, u32 name_count); VAddr names, u32 name_count);
ResultCode GetResourceLimitLimitValues(VAddr values, Handle resource_limit_handle, VAddr names, ResultCode GetResourceLimitLimitValues(VAddr values, Handle resource_limit_handle, VAddr names,
u32 name_count); u32 name_count);
ResultCode SetResourceLimitLimitValues(Handle res_limit, VAddr names, VAddr resource_list,
u32 name_count);
ResultCode CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr stack_top, ResultCode CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr stack_top,
u32 priority, s32 processor_id); u32 priority, s32 processor_id);
void ExitThread(); void ExitThread();
@ -1070,9 +1073,18 @@ ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_co
/// Create an address arbiter (to allocate access to shared resources) /// Create an address arbiter (to allocate access to shared resources)
ResultCode SVC::CreateAddressArbiter(Handle* out_handle) { ResultCode SVC::CreateAddressArbiter(Handle* out_handle) {
std::shared_ptr<AddressArbiter> arbiter = kernel.CreateAddressArbiter(); // Update address arbiter count in resource limit.
CASCADE_RESULT(*out_handle, const auto current_process = kernel.GetCurrentProcess();
kernel.GetCurrentProcess()->handle_table.Create(std::move(arbiter))); const auto& resource_limit = current_process->resource_limit;
if (!resource_limit->Reserve(ResourceLimitType::AddressArbiter, 1)) {
return ResultCode(ErrCodes::OutOfAddressArbiters, ErrorModule::OS,
ErrorSummary::OutOfResource, ErrorLevel::Status);
}
// Create address arbiter.
const auto arbiter = kernel.CreateAddressArbiter();
arbiter->resource_limit = resource_limit;
CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(arbiter)));
LOG_TRACE(Kernel_SVC, "returned handle=0x{:08X}", *out_handle); LOG_TRACE(Kernel_SVC, "returned handle=0x{:08X}", *out_handle);
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -1161,14 +1173,15 @@ ResultCode SVC::GetResourceLimitCurrentValues(VAddr values, Handle resource_limi
LOG_TRACE(Kernel_SVC, "called resource_limit={:08X}, names={:08X}, name_count={}", LOG_TRACE(Kernel_SVC, "called resource_limit={:08X}, names={:08X}, name_count={}",
resource_limit_handle, names, name_count); resource_limit_handle, names, name_count);
std::shared_ptr<ResourceLimit> resource_limit = const auto resource_limit =
kernel.GetCurrentProcess()->handle_table.Get<ResourceLimit>(resource_limit_handle); kernel.GetCurrentProcess()->handle_table.Get<ResourceLimit>(resource_limit_handle);
if (resource_limit == nullptr) if (!resource_limit) {
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
}
for (unsigned int i = 0; i < name_count; ++i) { for (u32 i = 0; i < name_count; ++i) {
u32 name = memory.Read32(names + i * sizeof(u32)); const u32 name = memory.Read32(names + i * sizeof(u32));
s64 value = resource_limit->GetCurrentResourceValue(name); const s64 value = resource_limit->GetCurrentValue(static_cast<ResourceLimitType>(name));
memory.Write64(values + i * sizeof(u64), value); memory.Write64(values + i * sizeof(u64), value);
} }
@ -1181,20 +1194,55 @@ ResultCode SVC::GetResourceLimitLimitValues(VAddr values, Handle resource_limit_
LOG_TRACE(Kernel_SVC, "called resource_limit={:08X}, names={:08X}, name_count={}", LOG_TRACE(Kernel_SVC, "called resource_limit={:08X}, names={:08X}, name_count={}",
resource_limit_handle, names, name_count); resource_limit_handle, names, name_count);
std::shared_ptr<ResourceLimit> resource_limit = const auto resource_limit =
kernel.GetCurrentProcess()->handle_table.Get<ResourceLimit>(resource_limit_handle); kernel.GetCurrentProcess()->handle_table.Get<ResourceLimit>(resource_limit_handle);
if (resource_limit == nullptr) if (!resource_limit) {
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
}
for (unsigned int i = 0; i < name_count; ++i) { for (u32 i = 0; i < name_count; ++i) {
u32 name = memory.Read32(names + i * sizeof(u32)); const auto name = static_cast<ResourceLimitType>(memory.Read32(names + i * sizeof(u32)));
s64 value = resource_limit->GetMaxResourceValue(name); if (name >= ResourceLimitType::Max) {
return ERR_INVALID_ENUM_VALUE;
}
const s64 value = resource_limit->GetLimitValue(name);
memory.Write64(values + i * sizeof(u64), value); memory.Write64(values + i * sizeof(u64), value);
} }
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
ResultCode SVC::SetResourceLimitLimitValues(Handle res_limit, VAddr names, VAddr resource_list,
u32 name_count) {
LOG_TRACE(Kernel_SVC, "called resource_limit={:08X}, names={:08X}, name_count={}", res_limit,
names, name_count);
const auto resource_limit =
kernel.GetCurrentProcess()->handle_table.Get<ResourceLimit>(res_limit);
if (!resource_limit) {
return ERR_INVALID_HANDLE;
}
for (u32 i = 0; i < name_count; ++i) {
const auto name = static_cast<ResourceLimitType>(memory.Read32(names + i * sizeof(u32)));
if (name >= ResourceLimitType::Max) {
return ERR_INVALID_ENUM_VALUE;
}
const s64 value = memory.Read64(resource_list + i * sizeof(u64));
const s32 value_high = value >> 32;
if (value_high < 0) {
return ERR_OUT_OF_RANGE_KERNEL;
}
if (name == ResourceLimitType::Commit && value_high != 0) {
auto& config_mem = kernel.GetConfigMemHandler().GetConfigMem();
config_mem.app_mem_alloc = value_high;
}
resource_limit->SetLimitValue(name, static_cast<s32>(value));
}
return RESULT_SUCCESS;
}
/// Creates a new thread /// Creates a new thread
ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr stack_top, ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr stack_top,
u32 priority, s32 processor_id) { u32 priority, s32 processor_id) {
@ -1204,11 +1252,10 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr
return ERR_OUT_OF_RANGE; return ERR_OUT_OF_RANGE;
} }
std::shared_ptr<Process> current_process = kernel.GetCurrentProcess(); const auto current_process = kernel.GetCurrentProcess();
const auto& resource_limit = current_process->resource_limit;
std::shared_ptr<ResourceLimit>& resource_limit = current_process->resource_limit; const u32 max_priority = resource_limit->GetLimitValue(ResourceLimitType::Priority);
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority && if (max_priority > priority && !current_process->no_thread_restrictions) {
!current_process->no_thread_restrictions) {
return ERR_NOT_AUTHORIZED; return ERR_NOT_AUTHORIZED;
} }
@ -1240,6 +1287,13 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr
break; break;
} }
// Update thread count in resource limit.
if (!resource_limit->Reserve(ResourceLimitType::Thread, 1)) {
return ResultCode(ErrCodes::OutOfThreads, ErrorModule::OS, ErrorSummary::OutOfResource,
ErrorLevel::Status);
}
// Create thread.
CASCADE_RESULT(std::shared_ptr<Thread> thread, CASCADE_RESULT(std::shared_ptr<Thread> thread,
kernel.CreateThread(name, entry_point, priority, arg, processor_id, stack_top, kernel.CreateThread(name, entry_point, priority, arg, processor_id, stack_top,
current_process)); current_process));
@ -1284,14 +1338,16 @@ ResultCode SVC::SetThreadPriority(Handle handle, u32 priority) {
return ERR_OUT_OF_RANGE; return ERR_OUT_OF_RANGE;
} }
std::shared_ptr<Thread> thread = kernel.GetCurrentProcess()->handle_table.Get<Thread>(handle); const auto thread = kernel.GetCurrentProcess()->handle_table.Get<Thread>(handle);
if (thread == nullptr) if (!thread) {
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
}
// Note: The kernel uses the current process's resource limit instead of // Note: The kernel uses the current process's resource limit instead of
// the one from the thread owner's resource limit. // the one from the thread owner's resource limit.
std::shared_ptr<ResourceLimit>& resource_limit = kernel.GetCurrentProcess()->resource_limit; const auto& resource_limit = kernel.GetCurrentProcess()->resource_limit;
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { const u32 max_priority = resource_limit->GetLimitValue(ResourceLimitType::Priority);
if (max_priority > priority) {
return ERR_NOT_AUTHORIZED; return ERR_NOT_AUTHORIZED;
} }
@ -1299,8 +1355,9 @@ ResultCode SVC::SetThreadPriority(Handle handle, u32 priority) {
thread->UpdatePriority(); thread->UpdatePriority();
// Update the mutexes that this thread is waiting for // Update the mutexes that this thread is waiting for
for (auto& mutex : thread->pending_mutexes) for (auto& mutex : thread->pending_mutexes) {
mutex->UpdatePriority(); mutex->UpdatePriority();
}
system.PrepareReschedule(); system.PrepareReschedule();
return RESULT_SUCCESS; return RESULT_SUCCESS;
@ -1308,9 +1365,19 @@ ResultCode SVC::SetThreadPriority(Handle handle, u32 priority) {
/// Create a mutex /// Create a mutex
ResultCode SVC::CreateMutex(Handle* out_handle, u32 initial_locked) { ResultCode SVC::CreateMutex(Handle* out_handle, u32 initial_locked) {
std::shared_ptr<Mutex> mutex = kernel.CreateMutex(initial_locked != 0); // Update mutex count in resource limit.
const auto current_process = kernel.GetCurrentProcess();
const auto& resource_limit = current_process->resource_limit;
if (!resource_limit->Reserve(ResourceLimitType::Mutex, 1)) {
return ResultCode(ErrCodes::OutOfMutexes, ErrorModule::OS, ErrorSummary::OutOfResource,
ErrorLevel::Status);
}
// Create mutex.
const auto mutex = kernel.CreateMutex(initial_locked != 0);
mutex->name = fmt::format("mutex-{:08x}", system.GetRunningCore().GetReg(14)); mutex->name = fmt::format("mutex-{:08x}", system.GetRunningCore().GetReg(14));
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(mutex))); mutex->resource_limit = resource_limit;
CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(mutex)));
LOG_TRACE(Kernel_SVC, "called initial_locked={} : created handle=0x{:08X}", LOG_TRACE(Kernel_SVC, "called initial_locked={} : created handle=0x{:08X}",
initial_locked ? "true" : "false", *out_handle); initial_locked ? "true" : "false", *out_handle);
@ -1373,11 +1440,20 @@ ResultCode SVC::GetThreadId(u32* thread_id, Handle handle) {
/// Creates a semaphore /// Creates a semaphore
ResultCode SVC::CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count) { ResultCode SVC::CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count) {
// Update semaphore count in resource limit.
const auto current_process = kernel.GetCurrentProcess();
const auto& resource_limit = current_process->resource_limit;
if (!resource_limit->Reserve(ResourceLimitType::Semaphore, 1)) {
return ResultCode(ErrCodes::OutOfSemaphores, ErrorModule::OS, ErrorSummary::OutOfResource,
ErrorLevel::Status);
}
// Create semaphore
CASCADE_RESULT(std::shared_ptr<Semaphore> semaphore, CASCADE_RESULT(std::shared_ptr<Semaphore> semaphore,
kernel.CreateSemaphore(initial_count, max_count)); kernel.CreateSemaphore(initial_count, max_count));
semaphore->name = fmt::format("semaphore-{:08x}", system.GetRunningCore().GetReg(14)); semaphore->name = fmt::format("semaphore-{:08x}", system.GetRunningCore().GetReg(14));
CASCADE_RESULT(*out_handle, semaphore->resource_limit = resource_limit;
kernel.GetCurrentProcess()->handle_table.Create(std::move(semaphore))); CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(semaphore)));
LOG_TRACE(Kernel_SVC, "called initial_count={}, max_count={}, created handle=0x{:08X}", LOG_TRACE(Kernel_SVC, "called initial_count={}, max_count={}, created handle=0x{:08X}",
initial_count, max_count, *out_handle); initial_count, max_count, *out_handle);
@ -1461,10 +1537,19 @@ ResultCode SVC::QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 ad
/// Create an event /// Create an event
ResultCode SVC::CreateEvent(Handle* out_handle, u32 reset_type) { ResultCode SVC::CreateEvent(Handle* out_handle, u32 reset_type) {
std::shared_ptr<Event> evt = // Update event count in resource limit.
kernel.CreateEvent(static_cast<ResetType>(reset_type), const auto current_process = kernel.GetCurrentProcess();
fmt::format("event-{:08x}", system.GetRunningCore().GetReg(14))); const auto& resource_limit = current_process->resource_limit;
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(evt))); if (!resource_limit->Reserve(ResourceLimitType::Event, 1)) {
return ResultCode(ErrCodes::OutOfEvents, ErrorModule::OS, ErrorSummary::OutOfResource,
ErrorLevel::Status);
}
// Create event.
const auto name = fmt::format("event-{:08x}", system.GetRunningCore().GetReg(14));
const auto event = kernel.CreateEvent(static_cast<ResetType>(reset_type), name);
event->resource_limit = resource_limit;
CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(event)));
LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type, LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type,
*out_handle); *out_handle);
@ -1505,10 +1590,19 @@ ResultCode SVC::ClearEvent(Handle handle) {
/// Creates a timer /// Creates a timer
ResultCode SVC::CreateTimer(Handle* out_handle, u32 reset_type) { ResultCode SVC::CreateTimer(Handle* out_handle, u32 reset_type) {
std::shared_ptr<Timer> timer = // Update timer count in resource limit.
kernel.CreateTimer(static_cast<ResetType>(reset_type), const auto current_process = kernel.GetCurrentProcess();
fmt ::format("timer-{:08x}", system.GetRunningCore().GetReg(14))); const auto& resource_limit = current_process->resource_limit;
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(timer))); if (!resource_limit->Reserve(ResourceLimitType::Timer, 1)) {
return ResultCode(ErrCodes::OutOfTimers, ErrorModule::OS, ErrorSummary::OutOfResource,
ErrorLevel::Status);
}
// Create timer.
const auto name = fmt::format("timer-{:08x}", system.GetRunningCore().GetReg(14));
const auto timer = kernel.CreateTimer(static_cast<ResetType>(reset_type), name);
timer->resource_limit = resource_limit;
CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(timer)));
LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type, LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type,
*out_handle); *out_handle);
@ -1655,15 +1749,22 @@ ResultCode SVC::CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my
return ERR_INVALID_ADDRESS; return ERR_INVALID_ADDRESS;
} }
std::shared_ptr<Process> current_process = kernel.GetCurrentProcess(); // Update shared memory count in resource limit.
const auto current_process = kernel.GetCurrentProcess();
const auto& resource_limit = current_process->resource_limit;
if (!resource_limit->Reserve(ResourceLimitType::SharedMemory, 1)) {
return ResultCode(ErrCodes::OutOfSharedMems, ErrorModule::OS, ErrorSummary::OutOfResource,
ErrorLevel::Status);
}
// When trying to create a memory block with address = 0, // When trying to create a memory block with address = 0,
// if the process has the Shared Device Memory flag in the exheader, // if the process has the Shared Device Memory flag in the exheader,
// then we have to allocate from the same region as the caller process instead of the BASE // then we have to allocate from the same region as the caller process instead of the BASE
// region. // region.
MemoryRegion region = MemoryRegion::BASE; MemoryRegion region = MemoryRegion::BASE;
if (addr == 0 && current_process->flags.shared_device_mem) if (addr == 0 && current_process->flags.shared_device_mem) {
region = current_process->flags.memory_region; region = current_process->flags.memory_region;
}
CASCADE_RESULT(shared_memory, CASCADE_RESULT(shared_memory,
kernel.CreateSharedMemory( kernel.CreateSharedMemory(
@ -2224,7 +2325,7 @@ const std::array<SVC::FunctionDef, 180> SVC::SVC_Table{{
{0x76, &SVC::Wrap<&SVC::TerminateProcess>, "TerminateProcess"}, {0x76, &SVC::Wrap<&SVC::TerminateProcess>, "TerminateProcess"},
{0x77, nullptr, "SetProcessResourceLimits"}, {0x77, nullptr, "SetProcessResourceLimits"},
{0x78, nullptr, "CreateResourceLimit"}, {0x78, nullptr, "CreateResourceLimit"},
{0x79, nullptr, "SetResourceLimitValues"}, {0x79, &SVC::Wrap<&SVC::SetResourceLimitLimitValues>, "SetResourceLimitLimitValues"},
{0x7A, nullptr, "AddCodeSegment"}, {0x7A, nullptr, "AddCodeSegment"},
{0x7B, nullptr, "Backdoor"}, {0x7B, nullptr, "Backdoor"},
{0x7C, &SVC::Wrap<&SVC::KernelSetState>, "KernelSetState"}, {0x7C, &SVC::Wrap<&SVC::KernelSetState>, "KernelSetState"},

View file

@ -16,6 +16,7 @@
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/memory.h" #include "core/memory.h"
@ -59,7 +60,12 @@ void Thread::Acquire(Thread* thread) {
Thread::Thread(KernelSystem& kernel, u32 core_id) Thread::Thread(KernelSystem& kernel, u32 core_id)
: WaitObject(kernel), core_id(core_id), thread_manager(kernel.GetThreadManager(core_id)) {} : WaitObject(kernel), core_id(core_id), thread_manager(kernel.GetThreadManager(core_id)) {}
Thread::~Thread() = default; Thread::~Thread() {
auto process = owner_process.lock();
if (process) {
process->resource_limit->Release(ResourceLimitType::Thread, 1);
}
}
Thread* ThreadManager::GetCurrentThread() const { Thread* ThreadManager::GetCurrentThread() const {
return current_thread.get(); return current_thread.get();
@ -342,7 +348,7 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(
ErrorSummary::InvalidArgument, ErrorLevel::Permanent); ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
} }
auto thread{std::make_shared<Thread>(*this, processor_id)}; auto thread = std::make_shared<Thread>(*this, processor_id);
thread_managers[processor_id]->thread_list.push_back(thread); thread_managers[processor_id]->thread_list.push_back(thread);
thread_managers[processor_id]->ready_queue.prepare(priority); thread_managers[processor_id]->ready_queue.prepare(priority);

View file

@ -8,6 +8,8 @@
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/object.h" #include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h" #include "core/hle/kernel/timer.h"
@ -17,14 +19,17 @@ namespace Kernel {
Timer::Timer(KernelSystem& kernel) Timer::Timer(KernelSystem& kernel)
: WaitObject(kernel), kernel(kernel), timer_manager(kernel.GetTimerManager()) {} : WaitObject(kernel), kernel(kernel), timer_manager(kernel.GetTimerManager()) {}
Timer::~Timer() { Timer::~Timer() {
Cancel(); Cancel();
timer_manager.timer_callback_table.erase(callback_id); timer_manager.timer_callback_table.erase(callback_id);
if (resource_limit) {
resource_limit->Release(ResourceLimitType::Timer, 1);
}
} }
std::shared_ptr<Timer> KernelSystem::CreateTimer(ResetType reset_type, std::string name) { std::shared_ptr<Timer> KernelSystem::CreateTimer(ResetType reset_type, std::string name) {
auto timer{std::make_shared<Timer>(*this)}; auto timer = std::make_shared<Timer>(*this);
timer->reset_type = reset_type; timer->reset_type = reset_type;
timer->signaled = false; timer->signaled = false;
timer->name = std::move(name); timer->name = std::move(name);
@ -32,7 +37,6 @@ std::shared_ptr<Timer> KernelSystem::CreateTimer(ResetType reset_type, std::stri
timer->interval_delay = 0; timer->interval_delay = 0;
timer->callback_id = ++timer_manager->next_timer_callback_id; timer->callback_id = ++timer_manager->next_timer_callback_id;
timer_manager->timer_callback_table[timer->callback_id] = timer.get(); timer_manager->timer_callback_table[timer->callback_id] = timer.get();
return timer; return timer;
} }

View file

@ -44,6 +44,8 @@ private:
} }
}; };
class ResourceLimit;
class Timer final : public WaitObject { class Timer final : public WaitObject {
public: public:
explicit Timer(KernelSystem& kernel); explicit Timer(KernelSystem& kernel);
@ -96,6 +98,8 @@ public:
*/ */
void Signal(s64 cycles_late); void Signal(s64 cycles_late);
std::shared_ptr<ResourceLimit> resource_limit;
private: private:
ResetType reset_type; ///< The ResetType of this timer ResetType reset_type; ///< The ResetType of this timer
@ -123,6 +127,7 @@ private:
ar& signaled; ar& signaled;
ar& name; ar& name;
ar& callback_id; ar& callback_id;
ar& resource_limit;
} }
}; };

View file

@ -12,6 +12,7 @@
#include "core/hle/ipc_helpers.h" #include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/hle/service/ac/ac.h" #include "core/hle/service/ac/ac.h"
#include "core/hle/service/ac/ac_i.h" #include "core/hle/service/ac/ac_i.h"

View file

@ -34,6 +34,10 @@ namespace Service::FS {
enum class MediaType : u32; enum class MediaType : u32;
} }
namespace Kernel {
class Mutex;
}
namespace Service::AM { namespace Service::AM {
namespace ErrCodes { namespace ErrCodes {

View file

@ -8,6 +8,7 @@
#include <boost/serialization/shared_ptr.hpp> #include <boost/serialization/shared_ptr.hpp>
#include "core/global.h" #include "core/global.h"
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
namespace Core { namespace Core {

View file

@ -6,6 +6,7 @@
#include "common/archives.h" #include "common/archives.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/ipc_helpers.h" #include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/hle/service/csnd/csnd_snd.h" #include "core/hle/service/csnd/csnd_snd.h"

View file

@ -16,7 +16,7 @@ static const ResultCode ERROR_BUFFER_TOO_SMALL = // 0xE0E12C1F
ResultCode(static_cast<ErrorDescription>(31), ErrorModule::RO, ErrorSummary::InvalidArgument, ResultCode(static_cast<ErrorDescription>(31), ErrorModule::RO, ErrorSummary::InvalidArgument,
ErrorLevel::Usage); ErrorLevel::Usage);
static ResultCode CROFormatError(u32 description) { static constexpr ResultCode CROFormatError(u32 description) {
return ResultCode(static_cast<ErrorDescription>(description), ErrorModule::RO, return ResultCode(static_cast<ErrorDescription>(description), ErrorModule::RO,
ErrorSummary::WrongArgument, ErrorLevel::Permanent); ErrorSummary::WrongArgument, ErrorLevel::Permanent);
} }

View file

@ -17,6 +17,7 @@
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/semaphore.h" #include "core/hle/kernel/semaphore.h"
#include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h" #include "core/hle/kernel/server_session.h"

View file

@ -277,7 +277,7 @@ ResultStatus AppLoader_THREEDSX::Load(std::shared_ptr<Kernel::Process>& process)
// Attach the default resource limit (APPLICATION) to the process // Attach the default resource limit (APPLICATION) to the process
process->resource_limit = Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory( process->resource_limit = Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory(
Kernel::ResourceLimitCategory::APPLICATION); Kernel::ResourceLimitCategory::Application);
// On real HW this is done with FS:Reg, but we can be lazy // On real HW this is done with FS:Reg, but we can be lazy
auto fs_user = auto fs_user =

View file

@ -388,7 +388,7 @@ ResultStatus AppLoader_ELF::Load(std::shared_ptr<Kernel::Process>& process) {
// Attach the default resource limit (APPLICATION) to the process // Attach the default resource limit (APPLICATION) to the process
process->resource_limit = Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory( process->resource_limit = Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory(
Kernel::ResourceLimitCategory::APPLICATION); Kernel::ResourceLimitCategory::Application);
process->Run(48, Kernel::DEFAULT_STACK_SIZE); process->Run(48, Kernel::DEFAULT_STACK_SIZE);

View file

@ -3,12 +3,11 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <algorithm> #include <algorithm>
#include <codecvt>
#include <cstring> #include <cstring>
#include <locale>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <fmt/format.h> #include <fmt/format.h>
#include "common/literals.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/settings.h" #include "common/settings.h"
#include "common/string_util.h" #include "common/string_util.h"
@ -32,6 +31,7 @@
namespace Loader { namespace Loader {
using namespace Common::Literals;
static const u64 UPDATE_MASK = 0x0000000e00000000; static const u64 UPDATE_MASK = 0x0000000e00000000;
FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
@ -148,13 +148,41 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process)
codeset->entrypoint = codeset->CodeSegment().addr; codeset->entrypoint = codeset->CodeSegment().addr;
codeset->memory = std::move(code); codeset->memory = std::move(code);
process = Core::System::GetInstance().Kernel().CreateProcess(std::move(codeset)); auto& system = Core::System::GetInstance();
process = system.Kernel().CreateProcess(std::move(codeset));
// Attach a resource limit to the process based on the resource limit category // Attach a resource limit to the process based on the resource limit category
process->resource_limit = const auto category = static_cast<Kernel::ResourceLimitCategory>(
Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory( overlay_ncch->exheader_header.arm11_system_local_caps.resource_limit_category);
static_cast<Kernel::ResourceLimitCategory>( process->resource_limit = system.Kernel().ResourceLimit().GetForCategory(category);
overlay_ncch->exheader_header.arm11_system_local_caps.resource_limit_category));
// When running N3DS-unaware titles pm will lie about the amount of memory available.
// This means RESLIMIT_COMMIT = APPMEMALLOC doesn't correspond to the actual size of
// APPLICATION. See:
// https://github.com/LumaTeam/Luma3DS/blob/e2778a45/sysmodules/pm/source/launch.c#L237
auto& ncch_caps = overlay_ncch->exheader_header.arm11_system_local_caps;
const auto o3ds_mode = static_cast<Kernel::MemoryMode>(ncch_caps.system_mode.Value());
const auto n3ds_mode = static_cast<Kernel::New3dsMemoryMode>(ncch_caps.n3ds_mode);
const bool is_new_3ds = Settings::values.is_new_3ds.GetValue();
if (is_new_3ds && n3ds_mode == Kernel::New3dsMemoryMode::Legacy &&
category == Kernel::ResourceLimitCategory::Application) {
u64 new_limit = 0;
switch (o3ds_mode) {
case Kernel::MemoryMode::Prod:
new_limit = 64_MiB;
break;
case Kernel::MemoryMode::Dev1:
new_limit = 96_MiB;
break;
case Kernel::MemoryMode::Dev2:
new_limit = 80_MiB;
break;
default:
break;
}
process->resource_limit->SetLimitValue(Kernel::ResourceLimitType::Commit,
static_cast<s32>(new_limit));
}
// Set the default CPU core for this process // Set the default CPU core for this process
process->ideal_processor = process->ideal_processor =
@ -171,9 +199,7 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process)
u32 stack_size = overlay_ncch->exheader_header.codeset_info.stack_size; u32 stack_size = overlay_ncch->exheader_header.codeset_info.stack_size;
// On real HW this is done with FS:Reg, but we can be lazy // On real HW this is done with FS:Reg, but we can be lazy
auto fs_user = auto fs_user = system.ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
Core::System::GetInstance().ServiceManager().GetService<Service::FS::FS_USER>(
"fs:USER");
fs_user->RegisterProgramInfo(process->process_id, process->codeset->program_id, filepath); fs_user->RegisterProgramInfo(process->process_id, process->codeset->program_id, filepath);
Service::FS::FS_USER::ProductInfo product_info{}; Service::FS::FS_USER::ProductInfo product_info{};