From 59df319f48a8a67db717233e5b222a0b86f1eae4 Mon Sep 17 00:00:00 2001 From: GPUCode <47210458+GPUCode@users.noreply.github.com> Date: Mon, 4 Dec 2023 13:31:06 +0200 Subject: [PATCH] kernel: Improve accuracy of KResourceLimit emulation (#7221) * core: Refactor resource limits * svc: Implement SetResourceLimitLimitValues * Also correct existing name and add missing error codes --- src/core/hle/kernel/address_arbiter.cpp | 12 +- src/core/hle/kernel/address_arbiter.h | 3 + src/core/hle/kernel/errors.h | 7 + src/core/hle/kernel/event.cpp | 22 ++- src/core/hle/kernel/event.h | 5 +- src/core/hle/kernel/kernel.cpp | 4 + src/core/hle/kernel/kernel.h | 2 + src/core/hle/kernel/mutex.cpp | 18 +- src/core/hle/kernel/mutex.h | 3 + src/core/hle/kernel/process.cpp | 17 +- src/core/hle/kernel/resource_limit.cpp | 219 ++++++++++----------- src/core/hle/kernel/resource_limit.h | 121 +++--------- src/core/hle/kernel/semaphore.cpp | 17 +- src/core/hle/kernel/semaphore.h | 5 +- src/core/hle/kernel/shared_memory.cpp | 5 +- src/core/hle/kernel/svc.cpp | 177 +++++++++++++---- src/core/hle/kernel/thread.cpp | 10 +- src/core/hle/kernel/timer.cpp | 10 +- src/core/hle/kernel/timer.h | 5 + src/core/hle/service/ac/ac.cpp | 1 + src/core/hle/service/am/am.h | 4 + src/core/hle/service/boss/boss.h | 1 + src/core/hle/service/csnd/csnd_snd.cpp | 1 + src/core/hle/service/ldr_ro/cro_helper.cpp | 2 +- src/core/hle/service/sm/srv.cpp | 1 + src/core/loader/3dsx.cpp | 2 +- src/core/loader/elf.cpp | 2 +- src/core/loader/ncch.cpp | 46 ++++- 28 files changed, 421 insertions(+), 301 deletions(-) diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 4d1ab4909..5254fa148 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -9,6 +9,7 @@ #include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/thread.h" #include "core/memory.h" @@ -68,13 +69,16 @@ bool AddressArbiter::ResumeHighestPriorityThread(VAddr address) { AddressArbiter::AddressArbiter(KernelSystem& kernel) : Object(kernel), kernel(kernel), timeout_callback(std::make_shared(*this)) {} -AddressArbiter::~AddressArbiter() {} + +AddressArbiter::~AddressArbiter() { + if (resource_limit) { + resource_limit->Release(ResourceLimitType::AddressArbiter, 1); + } +} std::shared_ptr KernelSystem::CreateAddressArbiter(std::string name) { - auto address_arbiter{std::make_shared(*this)}; - + auto address_arbiter = std::make_shared(*this); address_arbiter->name = std::move(name); - return address_arbiter; } diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 1cc477042..dc8bfbf2d 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -25,6 +25,7 @@ namespace Kernel { class Thread; +class ResourceLimit; enum class ArbitrationType : u32 { Signal, @@ -51,6 +52,7 @@ public: return HANDLE_TYPE; } + std::shared_ptr resource_limit; std::string name; ///< Name of address arbiter object (optional) ResultCode ArbitrateAddress(std::shared_ptr thread, ArbitrationType type, VAddr address, @@ -86,6 +88,7 @@ private: ar& name; ar& waiting_threads; ar& timeout_callback; + ar& resource_limit; } }; diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 2e9d63c30..49255dfd1 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -9,6 +9,12 @@ namespace Kernel { namespace ErrCodes { enum { + OutOfSharedMems = 11, + OutOfThreads = 12, + OutOfMutexes = 13, + OutOfSemaphores = 14, + OutOfEvents = 15, + OutOfTimers = 16, OutOfHandles = 19, SessionClosedByRemote = 26, PortNameTooLong = 30, @@ -16,6 +22,7 @@ enum { NoPendingSessions = 35, WrongPermission = 46, InvalidBufferDescriptor = 48, + OutOfAddressArbiters = 51, MaxConnectionsReached = 52, CommandTooLarge = 54, }; diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 04536002b..f1bc7bd12 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -2,12 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include -#include #include "common/archives.h" #include "common/assert.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/thread.h" SERIALIZE_EXPORT_IMPL(Kernel::Event) @@ -15,16 +14,19 @@ SERIALIZE_EXPORT_IMPL(Kernel::Event) namespace Kernel { Event::Event(KernelSystem& kernel) : WaitObject(kernel) {} -Event::~Event() {} + +Event::~Event() { + if (resource_limit) { + resource_limit->Release(ResourceLimitType::Event, 1); + } +} std::shared_ptr KernelSystem::CreateEvent(ResetType reset_type, std::string name) { - auto evt{std::make_shared(*this)}; - - evt->signaled = false; - evt->reset_type = reset_type; - evt->name = std::move(name); - - return evt; + auto event = std::make_shared(*this); + event->signaled = false; + event->reset_type = reset_type; + event->name = std::move(name); + return event; } bool Event::ShouldWait(const Thread* thread) const { diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index 41a48bced..a1fd8876c 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -7,8 +7,8 @@ #include #include #include -#include "common/common_types.h" #include "core/hle/kernel/object.h" +#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/wait_object.h" namespace Kernel { @@ -45,6 +45,8 @@ public: void Signal(); void Clear(); + std::shared_ptr resource_limit; + private: ResetType reset_type; ///< Current ResetType @@ -60,6 +62,7 @@ private: ar& reset_type; ar& signaled; ar& name; + ar& resource_limit; } }; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 5bd40d20a..db0caf59c 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -137,6 +137,10 @@ const SharedPage::Handler& KernelSystem::GetSharedPageHandler() const { return *shared_page_handler; } +ConfigMem::Handler& KernelSystem::GetConfigMemHandler() { + return *config_mem_handler; +} + IPCDebugger::Recorder& KernelSystem::GetIPCRecorder() { return *ipc_recorder; } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 4f508df7e..e9f77be6c 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -295,6 +295,8 @@ public: SharedPage::Handler& GetSharedPageHandler(); const SharedPage::Handler& GetSharedPageHandler() const; + ConfigMem::Handler& GetConfigMemHandler(); + IPCDebugger::Recorder& GetIPCRecorder(); const IPCDebugger::Recorder& GetIPCRecorder() const; diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index cf2621ed8..5ce8abe49 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -2,15 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include #include "common/archives.h" #include "common/assert.h" #include "core/core.h" -#include "core/global.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/object.h" +#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/thread.h" SERIALIZE_EXPORT_IMPL(Kernel::Mutex) @@ -27,18 +26,23 @@ void ReleaseThreadMutexes(Thread* thread) { } Mutex::Mutex(KernelSystem& kernel) : WaitObject(kernel), kernel(kernel) {} -Mutex::~Mutex() {} + +Mutex::~Mutex() { + if (resource_limit) { + resource_limit->Release(ResourceLimitType::Mutex, 1); + } +} std::shared_ptr KernelSystem::CreateMutex(bool initial_locked, std::string name) { - auto mutex{std::make_shared(*this)}; - + auto mutex = std::make_shared(*this); mutex->lock_count = 0; mutex->name = std::move(name); mutex->holding_thread = nullptr; // Acquire mutex with current thread if initialized as locked - if (initial_locked) - mutex->Acquire(thread_managers[current_cpu->GetID()]->GetCurrentThread()); + if (initial_locked) { + mutex->Acquire(GetCurrentThreadManager().GetCurrentThread()); + } return mutex; } diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index db98aa8c9..5094b3c4f 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -12,6 +12,7 @@ #include #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" @@ -36,6 +37,7 @@ public: return HANDLE_TYPE; } + std::shared_ptr resource_limit; int lock_count; ///< Number of times the mutex has been acquired u32 priority; ///< The priority of the mutex, used for priority inheritance. std::string name; ///< Name of mutex (optional) @@ -71,6 +73,7 @@ private: ar& priority; ar& name; ar& holding_thread; + ar& resource_limit; } }; diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 9256d9421..f9032f299 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -185,6 +185,11 @@ void Process::Set3dsxKernelCaps() { void Process::Run(s32 main_thread_priority, u32 stack_size) { 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, MemoryState memory_state) { HeapAllocate(segment.addr, segment.size, permissions, memory_state, true); @@ -281,7 +286,7 @@ ResultVal Process::HeapAllocate(VAddr target, u32 size, VMAPermission per holding_memory += allocated_fcram; memory_used += size; - resource_limit->current_commit += size; + resource_limit->Reserve(ResourceLimitType::Commit, size); return target; } @@ -310,7 +315,7 @@ ResultCode Process::HeapFree(VAddr target, u32 size) { ASSERT(result.IsSuccess()); memory_used -= size; - resource_limit->current_commit -= size; + resource_limit->Release(ResourceLimitType::Commit, size); return RESULT_SUCCESS; } @@ -356,7 +361,7 @@ ResultVal Process::LinearAllocate(VAddr target, u32 size, VMAPermission p holding_memory += MemoryRegionInfo::Interval(physical_offset, physical_offset + size); memory_used += size; - resource_limit->current_commit += size; + resource_limit->Reserve(ResourceLimitType::Commit, size); LOG_DEBUG(Kernel, "Allocated at target={:08X}", target); return target; @@ -385,7 +390,7 @@ ResultCode Process::LinearFree(VAddr target, u32 size) { holding_memory -= MemoryRegionInfo::Interval(physical_offset, physical_offset + size); memory_used -= size; - resource_limit->current_commit -= size; + resource_limit->Release(ResourceLimitType::Commit, size); return RESULT_SUCCESS; } @@ -569,7 +574,7 @@ void Process::FreeAllMemory() { auto size = entry.upper() - entry.lower(); memory_region->Free(entry.lower(), size); memory_used -= size; - resource_limit->current_commit -= size; + resource_limit->Release(ResourceLimitType::Commit, size); } holding_memory.clear(); @@ -590,7 +595,7 @@ void Process::FreeAllMemory() { // leaks in these values still. 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}", - resource_limit->current_commit); + resource_limit->GetCurrentValue(ResourceLimitType::Commit)); } Kernel::Process::Process(KernelSystem& kernel) diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index ab58a6074..8b57f22c4 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp @@ -2,10 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include #include "common/archives.h" #include "common/assert.h" -#include "common/logging/log.h" #include "common/settings.h" #include "core/hle/kernel/resource_limit.h" @@ -14,145 +12,130 @@ SERIALIZE_EXPORT_IMPL(Kernel::ResourceLimit) namespace Kernel { ResourceLimit::ResourceLimit(KernelSystem& kernel) : Object(kernel) {} -ResourceLimit::~ResourceLimit() {} + +ResourceLimit::~ResourceLimit() = default; std::shared_ptr ResourceLimit::Create(KernelSystem& kernel, std::string name) { - auto resource_limit{std::make_shared(kernel)}; - - resource_limit->name = std::move(name); + auto resource_limit = std::make_shared(kernel); + resource_limit->m_name = std::move(name); return resource_limit; } -std::shared_ptr ResourceLimitList::GetForCategory(ResourceLimitCategory category) { - switch (category) { - case ResourceLimitCategory::APPLICATION: - case ResourceLimitCategory::SYS_APPLET: - case ResourceLimitCategory::LIB_APPLET: - case ResourceLimitCategory::OTHER: - return resource_limits[static_cast(category)]; - default: - LOG_CRITICAL(Kernel, "Unknown resource limit category"); - UNREACHABLE(); - } +s32 ResourceLimit::GetCurrentValue(ResourceLimitType type) const { + const auto index = static_cast(type); + return m_current_values[index]; } -s32 ResourceLimit::GetCurrentResourceValue(u32 resource) const { - switch (resource) { - case COMMIT: - 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; - } +s32 ResourceLimit::GetLimitValue(ResourceLimitType type) const { + const auto index = static_cast(type); + return m_limit_values[index]; } -u32 ResourceLimit::GetMaxResourceValue(u32 resource) const { - switch (resource) { - case PRIORITY: - 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; +void ResourceLimit::SetLimitValue(ResourceLimitType type, s32 value) { + const auto index = static_cast(type); + m_limit_values[index] = value; +} + +bool ResourceLimit::Reserve(ResourceLimitType type, s32 amount) { + const auto index = static_cast(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(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) { - // Create the four resource limits that the system uses - // Create the APPLICATION resource limit + // PM makes APPMEMALLOC always match app RESLIMIT_COMMIT. + // 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 auto& appmemalloc = kernel.GetMemoryRegion(MemoryRegion::APPLICATION); - std::shared_ptr resource_limit = ResourceLimit::Create(kernel, "Applications"); - resource_limit->max_priority = 0x18; - resource_limit->max_commit = is_new_3ds ? 0x7C00000 : 0x4000000; - resource_limit->max_threads = 0x20; - resource_limit->max_events = 0x20; - resource_limit->max_mutexes = 0x20; - resource_limit->max_semaphores = 0x8; - resource_limit->max_timers = 0x8; - resource_limit->max_shared_mems = 0x10; - resource_limit->max_address_arbiters = 0x2; - resource_limit->max_cpu_time = 0x0; - resource_limits[static_cast(ResourceLimitCategory::APPLICATION)] = resource_limit; + // Create the Application resource limit + auto resource_limit = ResourceLimit::Create(kernel, "Applications"); + resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x18); + resource_limit->SetLimitValue(ResourceLimitType::Commit, appmemalloc->size); + resource_limit->SetLimitValue(ResourceLimitType::Thread, 0x20); + resource_limit->SetLimitValue(ResourceLimitType::Event, 0x20); + resource_limit->SetLimitValue(ResourceLimitType::Mutex, 0x20); + resource_limit->SetLimitValue(ResourceLimitType::Semaphore, 0x8); + resource_limit->SetLimitValue(ResourceLimitType::Timer, 0x8); + resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, 0x10); + resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, 0x2); + resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x0); + resource_limits[static_cast(ResourceLimitCategory::Application)] = resource_limit; - // Create the SYS_APPLET resource limit + // Create the SysApplet resource limit resource_limit = ResourceLimit::Create(kernel, "System Applets"); - resource_limit->max_priority = 0x4; - resource_limit->max_commit = is_new_3ds ? 0x5E06000 : 0x2606000; - resource_limit->max_threads = is_new_3ds ? 0x1D : 0xE; - resource_limit->max_events = is_new_3ds ? 0xB : 0x8; - resource_limit->max_mutexes = 0x8; - resource_limit->max_semaphores = 0x4; - resource_limit->max_timers = 0x4; - resource_limit->max_shared_mems = 0x8; - resource_limit->max_address_arbiters = 0x3; - resource_limit->max_cpu_time = 0x2710; - resource_limits[static_cast(ResourceLimitCategory::SYS_APPLET)] = resource_limit; + resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x4); + resource_limit->SetLimitValue(ResourceLimitType::Commit, is_new_3ds ? 0x5E06000 : 0x2606000); + resource_limit->SetLimitValue(ResourceLimitType::Thread, is_new_3ds ? 0x1D : 0xE); + resource_limit->SetLimitValue(ResourceLimitType::Event, is_new_3ds ? 0xB : 0x8); + resource_limit->SetLimitValue(ResourceLimitType::Mutex, 0x8); + resource_limit->SetLimitValue(ResourceLimitType::Semaphore, 0x4); + resource_limit->SetLimitValue(ResourceLimitType::Timer, 0x4); + resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, 0x8); + resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, 0x3); + resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x2710); + resource_limits[static_cast(ResourceLimitCategory::SysApplet)] = resource_limit; - // Create the LIB_APPLET resource limit + // Create the LibApplet resource limit resource_limit = ResourceLimit::Create(kernel, "Library Applets"); - resource_limit->max_priority = 0x4; - resource_limit->max_commit = 0x602000; - resource_limit->max_threads = 0xE; - resource_limit->max_events = 0x8; - resource_limit->max_mutexes = 0x8; - resource_limit->max_semaphores = 0x4; - resource_limit->max_timers = 0x4; - resource_limit->max_shared_mems = 0x8; - resource_limit->max_address_arbiters = 0x1; - resource_limit->max_cpu_time = 0x2710; - resource_limits[static_cast(ResourceLimitCategory::LIB_APPLET)] = resource_limit; + resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x4); + resource_limit->SetLimitValue(ResourceLimitType::Commit, 0x602000); + resource_limit->SetLimitValue(ResourceLimitType::Thread, 0xE); + resource_limit->SetLimitValue(ResourceLimitType::Event, 0x8); + resource_limit->SetLimitValue(ResourceLimitType::Mutex, 0x8); + resource_limit->SetLimitValue(ResourceLimitType::Semaphore, 0x4); + resource_limit->SetLimitValue(ResourceLimitType::Timer, 0x4); + resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, 0x8); + resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, 0x1); + resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x2710); + resource_limits[static_cast(ResourceLimitCategory::LibApplet)] = resource_limit; - // Create the OTHER resource limit + // Create the Other resource limit resource_limit = ResourceLimit::Create(kernel, "Others"); - resource_limit->max_priority = 0x4; - resource_limit->max_commit = is_new_3ds ? 0x2182000 : 0x1682000; - resource_limit->max_threads = is_new_3ds ? 0xE1 : 0xCA; - resource_limit->max_events = is_new_3ds ? 0x108 : 0xF8; - resource_limit->max_mutexes = is_new_3ds ? 0x25 : 0x23; - resource_limit->max_semaphores = is_new_3ds ? 0x43 : 0x40; - resource_limit->max_timers = is_new_3ds ? 0x2C : 0x2B; - resource_limit->max_shared_mems = is_new_3ds ? 0x1F : 0x1E; - resource_limit->max_address_arbiters = is_new_3ds ? 0x2D : 0x2B; - resource_limit->max_cpu_time = 0x3E8; - resource_limits[static_cast(ResourceLimitCategory::OTHER)] = resource_limit; + resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x4); + resource_limit->SetLimitValue(ResourceLimitType::Commit, is_new_3ds ? 0x2182000 : 0x1682000); + resource_limit->SetLimitValue(ResourceLimitType::Thread, is_new_3ds ? 0xE1 : 0xCA); + resource_limit->SetLimitValue(ResourceLimitType::Event, is_new_3ds ? 0x108 : 0xF8); + resource_limit->SetLimitValue(ResourceLimitType::Mutex, is_new_3ds ? 0x25 : 0x23); + resource_limit->SetLimitValue(ResourceLimitType::Semaphore, is_new_3ds ? 0x43 : 0x40); + resource_limit->SetLimitValue(ResourceLimitType::Timer, is_new_3ds ? 0x2C : 0x2B); + resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, is_new_3ds ? 0x1F : 0x1E); + resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, is_new_3ds ? 0x2D : 0x2B); + resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x3E8); + resource_limits[static_cast(ResourceLimitCategory::Other)] = resource_limit; } ResourceLimitList::~ResourceLimitList() = default; +std::shared_ptr ResourceLimitList::GetForCategory(ResourceLimitCategory category) { + switch (category) { + case ResourceLimitCategory::Application: + case ResourceLimitCategory::SysApplet: + case ResourceLimitCategory::LibApplet: + case ResourceLimitCategory::Other: + return resource_limits[static_cast(category)]; + default: + UNREACHABLE_MSG("Unknown resource limit category"); + } +} + } // namespace Kernel diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index cb1c8c78e..cfc9dae0a 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -16,23 +16,24 @@ namespace Kernel { enum class ResourceLimitCategory : u8 { - APPLICATION = 0, - SYS_APPLET = 1, - LIB_APPLET = 2, - OTHER = 3 + Application = 0, + SysApplet = 1, + LibApplet = 2, + Other = 3, }; -enum ResourceTypes { - PRIORITY = 0, - COMMIT = 1, - THREAD = 2, - EVENT = 3, - MUTEX = 4, - SEMAPHORE = 5, - TIMER = 6, - SHARED_MEMORY = 7, - ADDRESS_ARBITER = 8, - CPU_TIME = 9, +enum class ResourceLimitType : u32 { + Priority = 0, + Commit = 1, + Thread = 2, + Event = 3, + Mutex = 4, + Semaphore = 5, + Timer = 6, + SharedMemory = 7, + AddressArbiter = 8, + CpuTime = 9, + Max = 10, }; class ResourceLimit final : public Object { @@ -50,7 +51,7 @@ public: return "ResourceLimit"; } std::string GetName() const override { - return name; + return m_name; } static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit; @@ -58,90 +59,28 @@ public: return HANDLE_TYPE; } - /** - * Gets the current value for the specified resource. - * @param resource Requested resource type - * @returns The current value of the resource type - */ - s32 GetCurrentResourceValue(u32 resource) const; + s32 GetCurrentValue(ResourceLimitType type) const; + s32 GetLimitValue(ResourceLimitType type) const; - /** - * 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; + void SetLimitValue(ResourceLimitType name, s32 value); - /// Name of resource limit object. - std::string name; + bool Reserve(ResourceLimitType type, s32 amount); + bool Release(ResourceLimitType type, s32 amount); - /// Max thread priority that a process in this category can create - s32 max_priority = 0; - - /// Max memory that processes in this category can use - s32 max_commit = 0; - - ///< 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: + using ResourceArray = std::array(ResourceLimitType::Max)>; + ResourceArray m_limit_values{}; + ResourceArray m_current_values{}; + std::string m_name; private: friend class boost::serialization::access; template void serialize(Archive& ar, const unsigned int file_version) { ar& boost::serialization::base_object(*this); - // NB most of these aren't used at all currently, but we're adding them here for forwards - // compatibility - ar& name; - 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; + ar& m_name; + ar& m_limit_values; + ar& m_current_values; } }; diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index cbc6beea1..5e2412fe6 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -3,9 +3,10 @@ // Refer to the license.txt file included. #include "common/archives.h" -#include "common/assert.h" #include "core/hle/kernel/errors.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/thread.h" @@ -14,23 +15,27 @@ SERIALIZE_EXPORT_IMPL(Kernel::Semaphore) namespace Kernel { Semaphore::Semaphore(KernelSystem& kernel) : WaitObject(kernel) {} -Semaphore::~Semaphore() {} + +Semaphore::~Semaphore() { + if (resource_limit) { + resource_limit->Release(ResourceLimitType::Semaphore, 1); + } +} ResultVal> KernelSystem::CreateSemaphore(s32 initial_count, s32 max_count, std::string name) { - if (initial_count > max_count) + if (initial_count > max_count) { return ERR_INVALID_COMBINATION_KERNEL; - - auto semaphore{std::make_shared(*this)}; + } // When the semaphore is created, some slots are reserved for other threads, // and the rest is reserved for the caller thread + auto semaphore = std::make_shared(*this); semaphore->max_count = max_count; semaphore->available_count = initial_count; semaphore->name = std::move(name); - return semaphore; } diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h index b9863a838..2fa3b720b 100644 --- a/src/core/hle/kernel/semaphore.h +++ b/src/core/hle/kernel/semaphore.h @@ -8,7 +8,6 @@ #include #include #include -#include #include "common/common_types.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/wait_object.h" @@ -16,6 +15,8 @@ namespace Kernel { +class ResourceLimit; + class Semaphore final : public WaitObject { public: explicit Semaphore(KernelSystem& kernel); @@ -33,6 +34,7 @@ public: return HANDLE_TYPE; } + std::shared_ptr resource_limit; s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have s32 available_count; ///< Number of free slots left in the semaphore std::string name; ///< Name of semaphore (optional) @@ -55,6 +57,7 @@ private: ar& max_count; ar& available_count; ar& name; + ar& resource_limit; } }; diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index b86fd2ae5..46e216551 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -6,6 +6,7 @@ #include "common/logging/log.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/memory.h" +#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/shared_memory.h" #include "core/memory.h" @@ -14,6 +15,7 @@ SERIALIZE_EXPORT_IMPL(Kernel::SharedMemory) namespace Kernel { SharedMemory::SharedMemory(KernelSystem& kernel) : Object(kernel), kernel(kernel) {} + SharedMemory::~SharedMemory() { for (const auto& interval : holding_memory) { kernel.GetMemoryRegion(MemoryRegion::SYSTEM) @@ -22,6 +24,7 @@ SharedMemory::~SharedMemory() { auto process = owner_process.lock(); if (process) { + process->resource_limit->Release(ResourceLimitType::SharedMemory, 1); if (base_address != 0) { process->vm_manager.ChangeMemoryState(base_address, size, MemoryState::Locked, VMAPermission::None, MemoryState::Private, @@ -35,8 +38,8 @@ SharedMemory::~SharedMemory() { ResultVal> KernelSystem::CreateSharedMemory( std::shared_ptr owner_process, u32 size, MemoryPermission permissions, MemoryPermission other_permissions, VAddr address, MemoryRegion region, std::string name) { - auto shared_memory{std::make_shared(*this)}; + auto shared_memory = std::make_shared(*this); shared_memory->owner_process = owner_process; shared_memory->name = std::move(name); shared_memory->size = size; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 324864a7f..a9a1a618f 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -16,6 +16,7 @@ #include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/client_port.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/event.h" #include "core/hle/kernel/handle_table.h" @@ -390,6 +391,8 @@ private: VAddr names, u32 name_count); ResultCode GetResourceLimitLimitValues(VAddr values, Handle resource_limit_handle, VAddr names, 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, u32 priority, s32 processor_id); 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) ResultCode SVC::CreateAddressArbiter(Handle* out_handle) { - std::shared_ptr arbiter = kernel.CreateAddressArbiter(); - CASCADE_RESULT(*out_handle, - kernel.GetCurrentProcess()->handle_table.Create(std::move(arbiter))); + // Update address arbiter count in resource limit. + const auto current_process = kernel.GetCurrentProcess(); + 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); 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={}", resource_limit_handle, names, name_count); - std::shared_ptr resource_limit = + const auto resource_limit = kernel.GetCurrentProcess()->handle_table.Get(resource_limit_handle); - if (resource_limit == nullptr) + if (!resource_limit) { return ERR_INVALID_HANDLE; + } - for (unsigned int i = 0; i < name_count; ++i) { - u32 name = memory.Read32(names + i * sizeof(u32)); - s64 value = resource_limit->GetCurrentResourceValue(name); + for (u32 i = 0; i < name_count; ++i) { + const u32 name = memory.Read32(names + i * sizeof(u32)); + const s64 value = resource_limit->GetCurrentValue(static_cast(name)); 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={}", resource_limit_handle, names, name_count); - std::shared_ptr resource_limit = + const auto resource_limit = kernel.GetCurrentProcess()->handle_table.Get(resource_limit_handle); - if (resource_limit == nullptr) + if (!resource_limit) { return ERR_INVALID_HANDLE; + } - for (unsigned int i = 0; i < name_count; ++i) { - u32 name = memory.Read32(names + i * sizeof(u32)); - s64 value = resource_limit->GetMaxResourceValue(name); + for (u32 i = 0; i < name_count; ++i) { + const auto name = static_cast(memory.Read32(names + i * sizeof(u32))); + if (name >= ResourceLimitType::Max) { + return ERR_INVALID_ENUM_VALUE; + } + const s64 value = resource_limit->GetLimitValue(name); memory.Write64(values + i * sizeof(u64), value); } 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(res_limit); + if (!resource_limit) { + return ERR_INVALID_HANDLE; + } + + for (u32 i = 0; i < name_count; ++i) { + const auto name = static_cast(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(value)); + } + + return RESULT_SUCCESS; +} + /// Creates a new thread ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr stack_top, 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; } - std::shared_ptr current_process = kernel.GetCurrentProcess(); - - std::shared_ptr& resource_limit = current_process->resource_limit; - if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority && - !current_process->no_thread_restrictions) { + const auto current_process = kernel.GetCurrentProcess(); + const auto& resource_limit = current_process->resource_limit; + const u32 max_priority = resource_limit->GetLimitValue(ResourceLimitType::Priority); + if (max_priority > priority && !current_process->no_thread_restrictions) { return ERR_NOT_AUTHORIZED; } @@ -1240,6 +1287,13 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr 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, kernel.CreateThread(name, entry_point, priority, arg, processor_id, stack_top, current_process)); @@ -1284,14 +1338,16 @@ ResultCode SVC::SetThreadPriority(Handle handle, u32 priority) { return ERR_OUT_OF_RANGE; } - std::shared_ptr thread = kernel.GetCurrentProcess()->handle_table.Get(handle); - if (thread == nullptr) + const auto thread = kernel.GetCurrentProcess()->handle_table.Get(handle); + if (!thread) { return ERR_INVALID_HANDLE; + } // Note: The kernel uses the current process's resource limit instead of // the one from the thread owner's resource limit. - std::shared_ptr& resource_limit = kernel.GetCurrentProcess()->resource_limit; - if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { + const auto& resource_limit = kernel.GetCurrentProcess()->resource_limit; + const u32 max_priority = resource_limit->GetLimitValue(ResourceLimitType::Priority); + if (max_priority > priority) { return ERR_NOT_AUTHORIZED; } @@ -1299,8 +1355,9 @@ ResultCode SVC::SetThreadPriority(Handle handle, u32 priority) { thread->UpdatePriority(); // Update the mutexes that this thread is waiting for - for (auto& mutex : thread->pending_mutexes) + for (auto& mutex : thread->pending_mutexes) { mutex->UpdatePriority(); + } system.PrepareReschedule(); return RESULT_SUCCESS; @@ -1308,9 +1365,19 @@ ResultCode SVC::SetThreadPriority(Handle handle, u32 priority) { /// Create a mutex ResultCode SVC::CreateMutex(Handle* out_handle, u32 initial_locked) { - std::shared_ptr 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)); - 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}", initial_locked ? "true" : "false", *out_handle); @@ -1373,11 +1440,20 @@ ResultCode SVC::GetThreadId(u32* thread_id, Handle handle) { /// Creates a semaphore 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, kernel.CreateSemaphore(initial_count, max_count)); semaphore->name = fmt::format("semaphore-{:08x}", system.GetRunningCore().GetReg(14)); - CASCADE_RESULT(*out_handle, - kernel.GetCurrentProcess()->handle_table.Create(std::move(semaphore))); + semaphore->resource_limit = resource_limit; + 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}", initial_count, max_count, *out_handle); @@ -1461,10 +1537,19 @@ ResultCode SVC::QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 ad /// Create an event ResultCode SVC::CreateEvent(Handle* out_handle, u32 reset_type) { - std::shared_ptr evt = - kernel.CreateEvent(static_cast(reset_type), - fmt::format("event-{:08x}", system.GetRunningCore().GetReg(14))); - CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(evt))); + // Update event count in resource limit. + const auto current_process = kernel.GetCurrentProcess(); + const auto& resource_limit = current_process->resource_limit; + 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(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, *out_handle); @@ -1505,10 +1590,19 @@ ResultCode SVC::ClearEvent(Handle handle) { /// Creates a timer ResultCode SVC::CreateTimer(Handle* out_handle, u32 reset_type) { - std::shared_ptr timer = - kernel.CreateTimer(static_cast(reset_type), - fmt ::format("timer-{:08x}", system.GetRunningCore().GetReg(14))); - CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(timer))); + // Update timer count in resource limit. + const auto current_process = kernel.GetCurrentProcess(); + const auto& resource_limit = current_process->resource_limit; + 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(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, *out_handle); @@ -1655,15 +1749,22 @@ ResultCode SVC::CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my return ERR_INVALID_ADDRESS; } - std::shared_ptr 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, // 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 // region. 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; + } CASCADE_RESULT(shared_memory, kernel.CreateSharedMemory( @@ -2224,7 +2325,7 @@ const std::array SVC::SVC_Table{{ {0x76, &SVC::Wrap<&SVC::TerminateProcess>, "TerminateProcess"}, {0x77, nullptr, "SetProcessResourceLimits"}, {0x78, nullptr, "CreateResourceLimit"}, - {0x79, nullptr, "SetResourceLimitValues"}, + {0x79, &SVC::Wrap<&SVC::SetResourceLimitLimitValues>, "SetResourceLimitLimitValues"}, {0x7A, nullptr, "AddCodeSegment"}, {0x7B, nullptr, "Backdoor"}, {0x7C, &SVC::Wrap<&SVC::KernelSetState>, "KernelSetState"}, diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 7059e2085..1daa7a185 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -16,6 +16,7 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/thread.h" #include "core/hle/result.h" #include "core/memory.h" @@ -59,7 +60,12 @@ void Thread::Acquire(Thread* thread) { Thread::Thread(KernelSystem& kernel, u32 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 { return current_thread.get(); @@ -342,7 +348,7 @@ ResultVal> KernelSystem::CreateThread( ErrorSummary::InvalidArgument, ErrorLevel::Permanent); } - auto thread{std::make_shared(*this, processor_id)}; + auto thread = std::make_shared(*this, processor_id); thread_managers[processor_id]->thread_list.push_back(thread); thread_managers[processor_id]->ready_queue.prepare(priority); diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index d970b0a2f..41ce6ef1a 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -8,6 +8,8 @@ #include "core/core.h" #include "core/hle/kernel/handle_table.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/timer.h" @@ -17,14 +19,17 @@ namespace Kernel { Timer::Timer(KernelSystem& kernel) : WaitObject(kernel), kernel(kernel), timer_manager(kernel.GetTimerManager()) {} + Timer::~Timer() { Cancel(); timer_manager.timer_callback_table.erase(callback_id); + if (resource_limit) { + resource_limit->Release(ResourceLimitType::Timer, 1); + } } std::shared_ptr KernelSystem::CreateTimer(ResetType reset_type, std::string name) { - auto timer{std::make_shared(*this)}; - + auto timer = std::make_shared(*this); timer->reset_type = reset_type; timer->signaled = false; timer->name = std::move(name); @@ -32,7 +37,6 @@ std::shared_ptr KernelSystem::CreateTimer(ResetType reset_type, std::stri timer->interval_delay = 0; timer->callback_id = ++timer_manager->next_timer_callback_id; timer_manager->timer_callback_table[timer->callback_id] = timer.get(); - return timer; } diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index d5af5e654..9eedb6a85 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h @@ -44,6 +44,8 @@ private: } }; +class ResourceLimit; + class Timer final : public WaitObject { public: explicit Timer(KernelSystem& kernel); @@ -96,6 +98,8 @@ public: */ void Signal(s64 cycles_late); + std::shared_ptr resource_limit; + private: ResetType reset_type; ///< The ResetType of this timer @@ -123,6 +127,7 @@ private: ar& signaled; ar& name; ar& callback_id; + ar& resource_limit; } }; diff --git a/src/core/hle/service/ac/ac.cpp b/src/core/hle/service/ac/ac.cpp index 84c989642..8c148aa21 100644 --- a/src/core/hle/service/ac/ac.cpp +++ b/src/core/hle/service/ac/ac.cpp @@ -12,6 +12,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/resource_limit.h" #include "core/hle/result.h" #include "core/hle/service/ac/ac.h" #include "core/hle/service/ac/ac_i.h" diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index a5409e319..9f115086c 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -34,6 +34,10 @@ namespace Service::FS { enum class MediaType : u32; } +namespace Kernel { +class Mutex; +} + namespace Service::AM { namespace ErrCodes { diff --git a/src/core/hle/service/boss/boss.h b/src/core/hle/service/boss/boss.h index e4000e851..fd4ee7ebb 100644 --- a/src/core/hle/service/boss/boss.h +++ b/src/core/hle/service/boss/boss.h @@ -8,6 +8,7 @@ #include #include "core/global.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/resource_limit.h" #include "core/hle/service/service.h" namespace Core { diff --git a/src/core/hle/service/csnd/csnd_snd.cpp b/src/core/hle/service/csnd/csnd_snd.cpp index 0db9a801a..112faa468 100644 --- a/src/core/hle/service/csnd/csnd_snd.cpp +++ b/src/core/hle/service/csnd/csnd_snd.cpp @@ -6,6 +6,7 @@ #include "common/archives.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/resource_limit.h" #include "core/hle/result.h" #include "core/hle/service/csnd/csnd_snd.h" diff --git a/src/core/hle/service/ldr_ro/cro_helper.cpp b/src/core/hle/service/ldr_ro/cro_helper.cpp index bdeb89d30..eb097b2e7 100644 --- a/src/core/hle/service/ldr_ro/cro_helper.cpp +++ b/src/core/hle/service/ldr_ro/cro_helper.cpp @@ -16,7 +16,7 @@ static const ResultCode ERROR_BUFFER_TOO_SMALL = // 0xE0E12C1F ResultCode(static_cast(31), ErrorModule::RO, ErrorSummary::InvalidArgument, ErrorLevel::Usage); -static ResultCode CROFormatError(u32 description) { +static constexpr ResultCode CROFormatError(u32 description) { return ResultCode(static_cast(description), ErrorModule::RO, ErrorSummary::WrongArgument, ErrorLevel::Permanent); } diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp index e93909331..1960bb7e4 100644 --- a/src/core/hle/service/sm/srv.cpp +++ b/src/core/hle/service/sm/srv.cpp @@ -17,6 +17,7 @@ #include "core/hle/kernel/errors.h" #include "core/hle/kernel/event.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/server_port.h" #include "core/hle/kernel/server_session.h" diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index b100533f9..d235783a9 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp @@ -277,7 +277,7 @@ ResultStatus AppLoader_THREEDSX::Load(std::shared_ptr& process) // Attach the default resource limit (APPLICATION) to the process 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 auto fs_user = diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 788c94134..dded22498 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -388,7 +388,7 @@ ResultStatus AppLoader_ELF::Load(std::shared_ptr& process) { // Attach the default resource limit (APPLICATION) to the process process->resource_limit = Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory( - Kernel::ResourceLimitCategory::APPLICATION); + Kernel::ResourceLimitCategory::Application); process->Run(48, Kernel::DEFAULT_STACK_SIZE); diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 04dcf42ce..5bbe85944 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -3,12 +3,11 @@ // Refer to the license.txt file included. #include -#include #include -#include #include #include #include +#include "common/literals.h" #include "common/logging/log.h" #include "common/settings.h" #include "common/string_util.h" @@ -32,6 +31,7 @@ namespace Loader { +using namespace Common::Literals; static const u64 UPDATE_MASK = 0x0000000e00000000; FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { @@ -148,13 +148,41 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr& process) codeset->entrypoint = codeset->CodeSegment().addr; 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 - process->resource_limit = - Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory( - static_cast( - overlay_ncch->exheader_header.arm11_system_local_caps.resource_limit_category)); + const auto category = static_cast( + overlay_ncch->exheader_header.arm11_system_local_caps.resource_limit_category); + process->resource_limit = system.Kernel().ResourceLimit().GetForCategory(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(ncch_caps.system_mode.Value()); + const auto n3ds_mode = static_cast(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(new_limit)); + } // Set the default CPU core for this process process->ideal_processor = @@ -171,9 +199,7 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr& process) 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 - auto fs_user = - Core::System::GetInstance().ServiceManager().GetService( - "fs:USER"); + auto fs_user = system.ServiceManager().GetService("fs:USER"); fs_user->RegisterProgramInfo(process->process_id, process->codeset->program_id, filepath); Service::FS::FS_USER::ProductInfo product_info{};