From ec9b6641b12aa04ae3d7782b0423037dbc1400ac 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: [PATCH] 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(-)

diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 8da5a5c86..b6e6f115e 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<Memory::SlabHeap<Memory::Page>>();
 
+        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 05e21830c..47b3ac57b 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 75304b961..8830d4e91 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<Session>(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 67d748561..2eadd51d7 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> 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 1d377ffe6..31d899e06 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<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit,
@@ -160,11 +161,17 @@ ResultVal<s64> 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<ClientSession> 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<KThread> 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 765f408c3..6b0fc1591 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> TransferMemory::Create(KernelCore& kernel,