From 48b09cc56d4208d3de5ea4ccd208597652802da7 Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Thu, 4 Feb 2021 20:06:54 -0500 Subject: kernel: KScopedReservation implementation This implements KScopedReservation, allowing resource limit reservations to be more HW accurate, and release upon failure without requiring too many conditionals. --- src/core/hle/kernel/process.cpp | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) (limited to 'src/core/hle/kernel/process.cpp') diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 39dc3898a9..05e21830cb 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -16,6 +16,7 @@ #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory/memory_block_manager.h" @@ -116,6 +117,9 @@ std::shared_ptr Process::Create(Core::System& system, std::string name, std::shared_ptr process = std::make_shared(system); process->name = std::move(name); + + // TODO: This is inaccurate + // The process should hold a reference to the kernel-wide resource limit. process->resource_limit = std::make_shared(kernel, system); process->status = ProcessStatus::Created; process->program_id = 0; @@ -154,6 +158,9 @@ void Process::DecrementThreadCount() { } u64 Process::GetTotalPhysicalMemoryAvailable() const { + // TODO: This is expected to always return the application memory pool size after accurately + // reserving kernel resources. The current workaround uses a process-local resource limit of + // application memory pool size, which is inaccurate. const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size + main_thread_stack_size}; @@ -263,6 +270,17 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, system_resource_size = metadata.GetSystemResourceSize(); image_size = code_size; + // Set initial resource limits + resource_limit->SetLimitValue( + LimitableResource::PhysicalMemory, + kernel.MemoryManager().GetSize(Memory::MemoryManager::Pool::Application)); + KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory, + code_size + system_resource_size); + if (!memory_reservation.Succeeded()) { + LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes", + code_size + system_resource_size); + return ERR_RESOURCE_LIMIT_EXCEEDED; + } // Initialize proces address space if (const ResultCode result{ page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, 0x8000000, @@ -304,24 +322,22 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, UNREACHABLE(); } - // Set initial resource limits - resource_limit->SetLimitValue( - LimitableResource::PhysicalMemory, - kernel.MemoryManager().GetSize(Memory::MemoryManager::Pool::Application)); resource_limit->SetLimitValue(LimitableResource::Threads, 608); resource_limit->SetLimitValue(LimitableResource::Events, 700); resource_limit->SetLimitValue(LimitableResource::TransferMemory, 128); resource_limit->SetLimitValue(LimitableResource::Sessions, 894); - ASSERT(resource_limit->Reserve(LimitableResource::PhysicalMemory, code_size)); // Create TLS region tls_region_address = CreateTLSRegion(); + memory_reservation.Commit(); return handle_table.SetSize(capabilities.GetHandleTableSize()); } void Process::Run(s32 main_thread_priority, u64 stack_size) { AllocateMainThreadStack(stack_size); + resource_limit->Reserve(LimitableResource::Threads, 1); + resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size); const std::size_t heap_capacity{memory_usage_capacity - main_thread_stack_size - image_size}; ASSERT(!page_table->SetHeapCapacity(heap_capacity).IsError()); @@ -329,8 +345,6 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) { ChangeStatus(ProcessStatus::Running); SetupMainThread(system, *this, main_thread_priority, main_thread_stack_top); - resource_limit->Reserve(LimitableResource::Threads, 1); - resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size); } void Process::PrepareForTermination() { @@ -357,6 +371,11 @@ void Process::PrepareForTermination() { FreeTLSRegion(tls_region_address); tls_region_address = 0; + if (resource_limit) { + resource_limit->Release(LimitableResource::PhysicalMemory, + main_thread_stack_size + image_size); + } + ChangeStatus(ProcessStatus::Exited); } -- cgit v1.2.3 From faae6b55956218d4cfd9578a2d79c2fc00d31560 Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Fri, 12 Feb 2021 19:05:24 -0500 Subject: kernel: More accurately reserve and release resources --- src/core/hle/kernel/kernel.cpp | 15 ++++++++++++--- src/core/hle/kernel/process.cpp | 3 ++- src/core/hle/kernel/session.cpp | 11 ++++++++++- src/core/hle/kernel/shared_memory.cpp | 4 +++- src/core/hle/kernel/svc.cpp | 21 +++++++++++++-------- src/core/hle/kernel/transfer_memory.cpp | 2 ++ 6 files changed, 42 insertions(+), 14 deletions(-) (limited to 'src/core/hle/kernel/process.cpp') diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 8da5a5c860..b6e6f115eb 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -141,11 +141,17 @@ struct KernelCore::Impl { ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 700).IsSuccess()); ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200) .IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 900).IsSuccess()); + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 933).IsSuccess()); - if (!system_resource_limit->Reserve(LimitableResource::PhysicalMemory, 0x60000)) { + // Derived from recent software updates. The kernel reserves 27MB + constexpr u64 kernel_size{0x1b00000}; + if (!system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size)) { UNREACHABLE(); } + // Reserve secure applet memory, introduced in firmware 5.0.0 + constexpr u64 secure_applet_memory_size{0x400000}; + ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, + secure_applet_memory_size)); } void InitializePreemption(KernelCore& kernel) { @@ -302,8 +308,11 @@ struct KernelCore::Impl { // Allocate slab heaps user_slab_heap_pages = std::make_unique>(); + constexpr u64 user_slab_heap_size{0x1ef000}; + // Reserve slab heaps + ASSERT( + system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size)); // Initialize slab heaps - constexpr u64 user_slab_heap_size{0x3de000}; user_slab_heap_pages->Initialize( system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase), user_slab_heap_size); diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 05e21830cb..47b3ac57b3 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -39,6 +39,7 @@ namespace { */ void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) { const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); + ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1)); auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0, owner_process.GetIdealCoreId(), stack_top, &owner_process); @@ -279,7 +280,7 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, if (!memory_reservation.Succeeded()) { LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes", code_size + system_resource_size); - return ERR_RESOURCE_LIMIT_EXCEEDED; + return ResultResourceLimitedExceeded; } // Initialize proces address space if (const ResultCode result{ diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp index 75304b9619..8830d4e916 100644 --- a/src/core/hle/kernel/session.cpp +++ b/src/core/hle/kernel/session.cpp @@ -4,15 +4,23 @@ #include "common/assert.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/server_session.h" #include "core/hle/kernel/session.h" namespace Kernel { Session::Session(KernelCore& kernel) : KSynchronizationObject{kernel} {} -Session::~Session() = default; +Session::~Session() { + // Release reserved resource when the Session pair was created. + kernel.GetSystemResourceLimit()->Release(LimitableResource::Sessions, 1); +} Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { + // Reserve a new session from the resource limit. + KScopedResourceReservation session_reservation(kernel.GetSystemResourceLimit(), + LimitableResource::Sessions); + ASSERT(session_reservation.Succeeded()); auto session{std::make_shared(kernel)}; auto client_session{Kernel::ClientSession::Create(kernel, session, name + "_Client").Unwrap()}; auto server_session{Kernel::ServerSession::Create(kernel, session, name + "_Server").Unwrap()}; @@ -21,6 +29,7 @@ Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { session->client = client_session; session->server = server_session; + session_reservation.Commit(); return std::make_pair(std::move(client_session), std::move(server_session)); } diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 67d7485618..2eadd51d74 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -14,7 +14,9 @@ namespace Kernel { SharedMemory::SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory) : Object{kernel}, device_memory{device_memory} {} -SharedMemory::~SharedMemory() = default; +SharedMemory::~SharedMemory() { + kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size); +} std::shared_ptr SharedMemory::Create( KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process, diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 1d377ffe69..31d899e068 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -138,6 +138,7 @@ ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr ds enum class ResourceLimitValueType { CurrentValue, LimitValue, + PeakValue, }; ResultVal RetrieveResourceLimitValue(Core::System& system, Handle resource_limit, @@ -160,11 +161,17 @@ ResultVal RetrieveResourceLimitValue(Core::System& system, Handle resource_ return ResultInvalidHandle; } - if (value_type == ResourceLimitValueType::CurrentValue) { + switch (value_type) { + case ResourceLimitValueType::CurrentValue: return MakeResult(resource_limit_object->GetCurrentValue(type)); + case ResourceLimitValueType::LimitValue: + return MakeResult(resource_limit_object->GetLimitValue(type)); + case ResourceLimitValueType::PeakValue: + return MakeResult(resource_limit_object->GetPeakValue(type)); + default: + LOG_ERROR(Kernel_SVC, "Invalid resource value_type: '{}'", value_type); + return ResultInvalidEnumValue; } - - return MakeResult(resource_limit_object->GetLimitValue(type)); } } // Anonymous namespace @@ -314,8 +321,6 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, return ResultNotFound; } - ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(LimitableResource::Sessions, 1)); - auto client_port = it->second; std::shared_ptr client_session; @@ -1522,7 +1527,7 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e system.CoreTiming().GetGlobalTimeNs().count() + 100000000); if (!thread_reservation.Succeeded()) { LOG_ERROR(Kernel_SVC, "Could not reserve a new thread"); - return ERR_RESOURCE_LIMIT_EXCEEDED; + return ResultResourceLimitedExceeded; } std::shared_ptr thread; @@ -1896,7 +1901,7 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd LimitableResource::TransferMemory); if (!trmem_reservation.Succeeded()) { LOG_ERROR(Kernel_SVC, "Could not reserve a new transfer memory"); - return ERR_RESOURCE_LIMIT_EXCEEDED; + return ResultResourceLimitedExceeded; } auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size, perms); @@ -2026,7 +2031,7 @@ static ResultCode SignalEvent(Core::System& system, Handle event_handle) { LimitableResource::Events); if (!event_reservation.Succeeded()) { LOG_ERROR(Kernel, "Could not reserve a new event"); - return ERR_RESOURCE_LIMIT_EXCEEDED; + return ResultResourceLimitedExceeded; } // Get the writable event. diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp index 765f408c3c..6b0fc1591d 100644 --- a/src/core/hle/kernel/transfer_memory.cpp +++ b/src/core/hle/kernel/transfer_memory.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" @@ -17,6 +18,7 @@ TransferMemory::TransferMemory(KernelCore& kernel, Core::Memory::Memory& memory) TransferMemory::~TransferMemory() { // Release memory region when transfer memory is destroyed Reset(); + owner_process->GetResourceLimit()->Release(LimitableResource::TransferMemory, 1); } std::shared_ptr TransferMemory::Create(KernelCore& kernel, -- cgit v1.2.3