Compare commits
	
		
			2 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 8f054c47f6 | ||
|  | a8d869c347 | 
| @@ -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<Callback>(*this)) {} | ||||
| AddressArbiter::~AddressArbiter() {} | ||||
|  | ||||
| AddressArbiter::~AddressArbiter() { | ||||
|     if (resource_limit) { | ||||
|         resource_limit->Release(ResourceLimitType::AddressArbiter, 1); | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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); | ||||
|  | ||||
|     return address_arbiter; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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<ResourceLimit> resource_limit; | ||||
|     std::string name; ///< Name of address arbiter object (optional) | ||||
|  | ||||
|     ResultCode ArbitrateAddress(std::shared_ptr<Thread> thread, ArbitrationType type, VAddr address, | ||||
| @@ -86,6 +88,7 @@ private: | ||||
|         ar& name; | ||||
|         ar& waiting_threads; | ||||
|         ar& timeout_callback; | ||||
|         ar& resource_limit; | ||||
|     } | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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, | ||||
| }; | ||||
|   | ||||
| @@ -2,12 +2,11 @@ | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <vector> | ||||
| #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<Event> KernelSystem::CreateEvent(ResetType reset_type, std::string name) { | ||||
|     auto evt{std::make_shared<Event>(*this)}; | ||||
|  | ||||
|     evt->signaled = false; | ||||
|     evt->reset_type = reset_type; | ||||
|     evt->name = std::move(name); | ||||
|  | ||||
|     return evt; | ||||
|     auto event = std::make_shared<Event>(*this); | ||||
|     event->signaled = false; | ||||
|     event->reset_type = reset_type; | ||||
|     event->name = std::move(name); | ||||
|     return event; | ||||
| } | ||||
|  | ||||
| bool Event::ShouldWait(const Thread* thread) const { | ||||
|   | ||||
| @@ -7,8 +7,8 @@ | ||||
| #include <boost/serialization/base_object.hpp> | ||||
| #include <boost/serialization/export.hpp> | ||||
| #include <boost/serialization/string.hpp> | ||||
| #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<ResourceLimit> resource_limit; | ||||
|  | ||||
| private: | ||||
|     ResetType reset_type; ///< Current ResetType | ||||
|  | ||||
| @@ -60,6 +62,7 @@ private: | ||||
|         ar& reset_type; | ||||
|         ar& signaled; | ||||
|         ar& name; | ||||
|         ar& resource_limit; | ||||
|     } | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -295,6 +295,8 @@ public: | ||||
|     SharedPage::Handler& GetSharedPageHandler(); | ||||
|     const SharedPage::Handler& GetSharedPageHandler() const; | ||||
|  | ||||
|     ConfigMem::Handler& GetConfigMemHandler(); | ||||
|  | ||||
|     IPCDebugger::Recorder& GetIPCRecorder(); | ||||
|     const IPCDebugger::Recorder& GetIPCRecorder() const; | ||||
|  | ||||
|   | ||||
| @@ -2,15 +2,14 @@ | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <vector> | ||||
| #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<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->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; | ||||
| } | ||||
|   | ||||
| @@ -12,6 +12,7 @@ | ||||
| #include <boost/serialization/string.hpp> | ||||
| #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<ResourceLimit> 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; | ||||
|     } | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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<VAddr> 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<VAddr> 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) | ||||
|   | ||||
| @@ -2,10 +2,8 @@ | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <cstring> | ||||
| #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> ResourceLimit::Create(KernelSystem& kernel, std::string name) { | ||||
|     auto resource_limit{std::make_shared<ResourceLimit>(kernel)}; | ||||
|  | ||||
|     resource_limit->name = std::move(name); | ||||
|     auto resource_limit = std::make_shared<ResourceLimit>(kernel); | ||||
|     resource_limit->m_name = std::move(name); | ||||
|     return resource_limit; | ||||
| } | ||||
|  | ||||
| std::shared_ptr<ResourceLimit> 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<u8>(category)]; | ||||
|     default: | ||||
|         LOG_CRITICAL(Kernel, "Unknown resource limit category"); | ||||
|         UNREACHABLE(); | ||||
|     } | ||||
| s32 ResourceLimit::GetCurrentValue(ResourceLimitType type) const { | ||||
|     const auto index = static_cast<size_t>(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<size_t>(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<size_t>(type); | ||||
|     m_limit_values[index] = value; | ||||
| } | ||||
|  | ||||
| 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) { | ||||
|     // 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<ResourceLimit> 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<u8>(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<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->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<u8>(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<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->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<u8>(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<u8>(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<u8>(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<u8>(ResourceLimitCategory::Other)] = resource_limit; | ||||
| } | ||||
|  | ||||
| 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 | ||||
|   | ||||
| @@ -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<s32, static_cast<size_t>(ResourceLimitType::Max)>; | ||||
|     ResourceArray m_limit_values{}; | ||||
|     ResourceArray m_current_values{}; | ||||
|     std::string m_name; | ||||
|  | ||||
| private: | ||||
|     friend class boost::serialization::access; | ||||
|     template <class Archive> | ||||
|     void serialize(Archive& ar, const unsigned int file_version) { | ||||
|         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 | ||||
|         // 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; | ||||
|     } | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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<std::shared_ptr<Semaphore>> 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<Semaphore>(*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<Semaphore>(*this); | ||||
|     semaphore->max_count = max_count; | ||||
|     semaphore->available_count = initial_count; | ||||
|     semaphore->name = std::move(name); | ||||
|  | ||||
|     return semaphore; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,6 @@ | ||||
| #include <boost/serialization/base_object.hpp> | ||||
| #include <boost/serialization/export.hpp> | ||||
| #include <boost/serialization/string.hpp> | ||||
| #include <queue> | ||||
| #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<ResourceLimit> 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; | ||||
|     } | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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<std::shared_ptr<SharedMemory>> KernelSystem::CreateSharedMemory( | ||||
|     std::shared_ptr<Process> owner_process, u32 size, MemoryPermission permissions, | ||||
|     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->name = std::move(name); | ||||
|     shared_memory->size = size; | ||||
|   | ||||
| @@ -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<AddressArbiter> 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<ResourceLimit> resource_limit = | ||||
|     const auto resource_limit = | ||||
|         kernel.GetCurrentProcess()->handle_table.Get<ResourceLimit>(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<ResourceLimitType>(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<ResourceLimit> resource_limit = | ||||
|     const auto resource_limit = | ||||
|         kernel.GetCurrentProcess()->handle_table.Get<ResourceLimit>(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<ResourceLimitType>(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<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 | ||||
| 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<Process> current_process = kernel.GetCurrentProcess(); | ||||
|  | ||||
|     std::shared_ptr<ResourceLimit>& 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> 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> thread = kernel.GetCurrentProcess()->handle_table.Get<Thread>(handle); | ||||
|     if (thread == nullptr) | ||||
|     const auto thread = kernel.GetCurrentProcess()->handle_table.Get<Thread>(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<ResourceLimit>& 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> 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> 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<Event> evt = | ||||
|         kernel.CreateEvent(static_cast<ResetType>(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<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, | ||||
|               *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> timer = | ||||
|         kernel.CreateTimer(static_cast<ResetType>(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<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, | ||||
|               *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<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, | ||||
|     // 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::FunctionDef, 180> 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"}, | ||||
|   | ||||
| @@ -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<std::shared_ptr<Thread>> KernelSystem::CreateThread( | ||||
|                           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]->ready_queue.prepare(priority); | ||||
|   | ||||
| @@ -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<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->signaled = false; | ||||
|     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->callback_id = ++timer_manager->next_timer_callback_id; | ||||
|     timer_manager->timer_callback_table[timer->callback_id] = timer.get(); | ||||
|  | ||||
|     return timer; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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<ResourceLimit> 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; | ||||
|     } | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
| @@ -34,6 +34,10 @@ namespace Service::FS { | ||||
| enum class MediaType : u32; | ||||
| } | ||||
|  | ||||
| namespace Kernel { | ||||
| class Mutex; | ||||
| } | ||||
|  | ||||
| namespace Service::AM { | ||||
|  | ||||
| namespace ErrCodes { | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| #include <boost/serialization/shared_ptr.hpp> | ||||
| #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 { | ||||
|   | ||||
| @@ -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" | ||||
|  | ||||
|   | ||||
| @@ -16,7 +16,7 @@ static const ResultCode ERROR_BUFFER_TOO_SMALL = // 0xE0E12C1F | ||||
|     ResultCode(static_cast<ErrorDescription>(31), ErrorModule::RO, ErrorSummary::InvalidArgument, | ||||
|                ErrorLevel::Usage); | ||||
|  | ||||
| static ResultCode CROFormatError(u32 description) { | ||||
| static constexpr ResultCode CROFormatError(u32 description) { | ||||
|     return ResultCode(static_cast<ErrorDescription>(description), ErrorModule::RO, | ||||
|                       ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||||
| } | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
| @@ -277,7 +277,7 @@ ResultStatus AppLoader_THREEDSX::Load(std::shared_ptr<Kernel::Process>& 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 = | ||||
|   | ||||
| @@ -388,7 +388,7 @@ ResultStatus AppLoader_ELF::Load(std::shared_ptr<Kernel::Process>& 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); | ||||
|  | ||||
|   | ||||
| @@ -3,12 +3,11 @@ | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <codecvt> | ||||
| #include <cstring> | ||||
| #include <locale> | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| #include <fmt/format.h> | ||||
| #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<Kernel::Process>& 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<Kernel::ResourceLimitCategory>( | ||||
|                     overlay_ncch->exheader_header.arm11_system_local_caps.resource_limit_category)); | ||||
|         const auto category = static_cast<Kernel::ResourceLimitCategory>( | ||||
|             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<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 | ||||
|         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; | ||||
|  | ||||
|         // On real HW this is done with FS:Reg, but we can be lazy | ||||
|         auto fs_user = | ||||
|             Core::System::GetInstance().ServiceManager().GetService<Service::FS::FS_USER>( | ||||
|                 "fs:USER"); | ||||
|         auto fs_user = system.ServiceManager().GetService<Service::FS::FS_USER>("fs:USER"); | ||||
|         fs_user->RegisterProgramInfo(process->process_id, process->codeset->program_id, filepath); | ||||
|  | ||||
|         Service::FS::FS_USER::ProductInfo product_info{}; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user