From 84a22cb5947b0733a043429032367227e73342dc Mon Sep 17 00:00:00 2001
From: Yuri Kunde Schlesner <yuriks@yuriks.net>
Date: Thu, 26 Nov 2015 19:00:16 -0800
Subject: [PATCH] Kernel: Implement svcGetSystemInfo

This makes smealum/ctrulib@b96dd51d3349961189d4ab1bc2a5c45deff21c09 work
with Citra.
---
 src/core/hle/function_wrappers.h |  8 ++++++
 src/core/hle/kernel/memory.cpp   |  2 ++
 src/core/hle/kernel/memory.h     |  1 +
 src/core/hle/kernel/process.cpp  |  6 ++++
 src/core/hle/kernel/thread.cpp   |  3 ++
 src/core/hle/svc.cpp             | 47 +++++++++++++++++++++++++++++++-
 src/core/hle/svc.h               | 29 ++++++++++++++++++++
 7 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 5846a161b..3501e45db 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -159,6 +159,14 @@ template<ResultCode func(s32*, u32, s32)> void Wrap() {
     FuncReturn(retval);
 }
 
+template<ResultCode func(s64*, u32, s32)> void Wrap() {
+    s64 param_1 = 0;
+    u32 retval = func(&param_1, PARAM(1), PARAM(2)).raw;
+    Core::g_app_core->SetReg(1, (u32)param_1);
+    Core::g_app_core->SetReg(2, (u32)(param_1 >> 32));
+    FuncReturn(retval);
+}
+
 template<ResultCode func(u32*, u32, u32, u32, u32)> void Wrap() {
     u32 param_1 = 0;
     u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw;
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
index e4fc5f3c4..0cfb43fc7 100644
--- a/src/core/hle/kernel/memory.cpp
+++ b/src/core/hle/kernel/memory.cpp
@@ -51,6 +51,7 @@ void MemoryInit(u32 mem_type) {
     for (int i = 0; i < 3; ++i) {
         memory_regions[i].base = base;
         memory_regions[i].size = memory_region_sizes[mem_type][i];
+        memory_regions[i].used = 0;
         memory_regions[i].linear_heap_memory = std::make_shared<std::vector<u8>>();
 
         base += memory_regions[i].size;
@@ -72,6 +73,7 @@ void MemoryShutdown() {
     for (auto& region : memory_regions) {
         region.base = 0;
         region.size = 0;
+        region.used = 0;
         region.linear_heap_memory = nullptr;
     }
 }
diff --git a/src/core/hle/kernel/memory.h b/src/core/hle/kernel/memory.h
index 36690b091..091c1f89f 100644
--- a/src/core/hle/kernel/memory.h
+++ b/src/core/hle/kernel/memory.h
@@ -17,6 +17,7 @@ class VMManager;
 struct MemoryRegionInfo {
     u32 base; // Not an address, but offset from start of FCRAM
     u32 size;
+    u32 used;
 
     std::shared_ptr<std::vector<u8>> linear_heap_memory;
 };
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index c2b4963d4..d148efde2 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -111,6 +111,7 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) {
                 segment.offset, segment.size, memory_state).Unwrap();
         vm_manager.Reprotect(vma, permissions);
         misc_memory_used += segment.size;
+        memory_region->used += segment.size;
     };
 
     // Map CodeSet segments
@@ -123,6 +124,7 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) {
             std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, MemoryState::Locked
             ).Unwrap();
     misc_memory_used += stack_size;
+    memory_region->used += stack_size;
 
     vm_manager.LogLayout(Log::Level::Debug);
     Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority);
@@ -165,6 +167,7 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u32 size, VMAPermission per
     vm_manager.Reprotect(vma, perms);
 
     heap_used += size;
+    memory_region->used += size;
 
     return MakeResult<VAddr>(heap_end - size);
 }
@@ -182,6 +185,7 @@ ResultCode Process::HeapFree(VAddr target, u32 size) {
     if (result.IsError()) return result;
 
     heap_used -= size;
+    memory_region->used -= size;
 
     return RESULT_SUCCESS;
 }
@@ -217,6 +221,7 @@ ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission p
     vm_manager.Reprotect(vma, perms);
 
     linear_heap_used += size;
+    memory_region->used += size;
 
     return MakeResult<VAddr>(target);
 }
@@ -243,6 +248,7 @@ ResultCode Process::LinearFree(VAddr target, u32 size) {
     if (result.IsError()) return result;
 
     linear_heap_used -= size;
+    memory_region->used -= size;
 
     if (target + size == heap_end) {
         // End of linear heap has been freed, so check what's the last allocated block in it and
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 00fa995f6..c08fc1c7a 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -20,6 +20,7 @@
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/process.h"
 #include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/memory.h"
 #include "core/hle/kernel/mutex.h"
 #include "core/hle/result.h"
 #include "core/memory.h"
@@ -118,6 +119,7 @@ void Thread::Stop() {
 
     Kernel::g_current_process->used_tls_slots[tls_index] = false;
     g_current_process->misc_memory_used -= Memory::TLS_ENTRY_SIZE;
+    g_current_process->memory_region->used -= Memory::TLS_ENTRY_SIZE;
 
     HLE::Reschedule(__func__);
 }
@@ -416,6 +418,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
 
     ASSERT_MSG(thread->tls_index != -1, "Out of TLS space");
     g_current_process->misc_memory_used += Memory::TLS_ENTRY_SIZE;
+    g_current_process->memory_region->used += Memory::TLS_ENTRY_SIZE;
 
     // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
     // to initialize the context
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 45d5f3c5d..7f63ff505 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -778,6 +778,51 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32
     return RESULT_SUCCESS;
 }
 
+static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) {
+    using Kernel::MemoryRegion;
+
+    LOG_TRACE(Kernel_SVC, "called process=0x%08X type=%u param=%d", process_handle, type, param);
+
+    switch ((SystemInfoType)type) {
+    case SystemInfoType::REGION_MEMORY_USAGE:
+        switch ((SystemInfoMemUsageRegion)param) {
+        case SystemInfoMemUsageRegion::ALL:
+            *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::APPLICATION)->used
+                 + Kernel::GetMemoryRegion(Kernel::MemoryRegion::SYSTEM)->used
+                 + Kernel::GetMemoryRegion(Kernel::MemoryRegion::BASE)->used;
+            break;
+        case SystemInfoMemUsageRegion::APPLICATION:
+            *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::APPLICATION)->used;
+            break;
+        case SystemInfoMemUsageRegion::SYSTEM:
+            *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::SYSTEM)->used;
+            break;
+        case SystemInfoMemUsageRegion::BASE:
+            *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::BASE)->used;
+            break;
+        default:
+            LOG_ERROR(Kernel_SVC, "unknown GetSystemInfo type=0 region: param=%d", param);
+            *out = 0;
+            break;
+        }
+        break;
+    case SystemInfoType::KERNEL_ALLOCATED_PAGES:
+        LOG_ERROR(Kernel_SVC, "unimplemented GetSystemInfo type=2 param=%d", type, param);
+        *out = 0;
+        break;
+    case SystemInfoType::KERNEL_SPAWNED_PIDS:
+        *out = 5;
+        break;
+    default:
+        LOG_ERROR(Kernel_SVC, "unknown GetSystemInfo type=%u param=%d", type, param);
+        *out = 0;
+        break;
+    }
+
+    // This function never returns an error, even if invalid parameters were passed.
+    return RESULT_SUCCESS;
+}
+
 static ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type) {
     LOG_TRACE(Kernel_SVC, "called process=0x%08X type=%u", process_handle, type);
 
@@ -877,7 +922,7 @@ static const FunctionDef SVC_Table[] = {
     {0x27, HLE::Wrap<DuplicateHandle>,      "DuplicateHandle"},
     {0x28, HLE::Wrap<GetSystemTick>,        "GetSystemTick"},
     {0x29, nullptr,                         "GetHandleInfo"},
-    {0x2A, nullptr,                         "GetSystemInfo"},
+    {0x2A, HLE::Wrap<GetSystemInfo>,        "GetSystemInfo"},
     {0x2B, HLE::Wrap<GetProcessInfo>,       "GetProcessInfo"},
     {0x2C, nullptr,                         "GetThreadInfo"},
     {0x2D, HLE::Wrap<ConnectToPort>,        "ConnectToPort"},
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h
index 12de9ffbe..4b9c71e06 100644
--- a/src/core/hle/svc.h
+++ b/src/core/hle/svc.h
@@ -41,6 +41,35 @@ enum ArbitrationType {
 
 namespace SVC {
 
+/// Values accepted by svcGetSystemInfo's type parameter.
+enum class SystemInfoType {
+    /**
+     * Reports total used memory for all regions or a specific one, according to the extra
+     * parameter. See `SystemInfoMemUsageRegion`.
+     */
+    REGION_MEMORY_USAGE = 0,
+    /**
+     * Returns the memory usage for certain allocations done internally by the kernel.
+     */
+    KERNEL_ALLOCATED_PAGES = 2,
+    /**
+     * "This returns the total number of processes which were launched directly by the kernel.
+     * For the ARM11 NATIVE_FIRM kernel, this is 5, for processes sm, fs, pm, loader, and pxi."
+     */
+    KERNEL_SPAWNED_PIDS = 26,
+};
+
+/**
+ * Accepted by svcGetSystemInfo param with REGION_MEMORY_USAGE type. Selects a region to query
+ * memory usage of.
+ */
+enum class SystemInfoMemUsageRegion {
+    ALL = 0,
+    APPLICATION = 1,
+    SYSTEM = 2,
+    BASE = 3,
+};
+
 void CallSVC(u32 immediate);
 
 } // namespace