From 8ad5f2c50648c333c3c46f89533e200a39bf6ca8 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Wed, 13 Dec 2023 15:32:25 -0500
Subject: [PATCH] common: use memory holepunching when clearing memory

---
 src/common/host_memory.cpp                | 38 +++++++++++++++++------
 src/common/host_memory.h                  |  2 ++
 src/core/hle/kernel/k_memory_manager.cpp  |  5 +--
 src/core/hle/kernel/k_page_table_base.cpp | 14 +++++----
 4 files changed, 42 insertions(+), 17 deletions(-)

diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index 4bfc64f2d..e540375b8 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -11,10 +11,6 @@
 
 #elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
 
-#ifdef ANDROID
-#include <android/sharedmem.h>
-#endif
-
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE
 #endif
@@ -193,6 +189,11 @@ public:
         }
     }
 
+    bool ClearBackingRegion(size_t physical_offset, size_t length) {
+        // TODO: This does not seem to be possible on Windows.
+        return false;
+    }
+
     void EnableDirectMappedAddress() {
         // TODO
         UNREACHABLE();
@@ -442,9 +443,7 @@ public:
         }
 
         // Backing memory initialization
-#ifdef ANDROID
-        fd = ASharedMemory_create("HostMemory", backing_size);
-#elif defined(__FreeBSD__) && __FreeBSD__ < 13
+#if defined(__FreeBSD__) && __FreeBSD__ < 13
         // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
         fd = shm_open(SHM_ANON, O_RDWR, 0600);
 #else
@@ -455,7 +454,6 @@ public:
             throw std::bad_alloc{};
         }
 
-#ifndef ANDROID
         // Defined to extend the file with zeros
         int ret = ftruncate(fd, backing_size);
         if (ret != 0) {
@@ -463,7 +461,6 @@ public:
                          strerror(errno));
             throw std::bad_alloc{};
         }
-#endif
 
         backing_base = static_cast<u8*>(
             mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
@@ -552,6 +549,19 @@ public:
         ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
     }
 
+    bool ClearBackingRegion(size_t physical_offset, size_t length) {
+#ifdef __linux__
+        // Set MADV_REMOVE on backing map to destroy it instantly.
+        // This also deletes the area from the backing file.
+        int ret = madvise(backing_base + physical_offset, length, MADV_REMOVE);
+        ASSERT_MSG(ret == 0, "madvise failed: {}", strerror(errno));
+
+        return true;
+#else
+        return false;
+#endif
+    }
+
     void EnableDirectMappedAddress() {
         virtual_base = nullptr;
     }
@@ -623,6 +633,10 @@ public:
 
     void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {}
 
+    bool ClearBackingRegion(size_t physical_offset, size_t length) {
+        return false;
+    }
+
     void EnableDirectMappedAddress() {}
 
     u8* backing_base{nullptr};
@@ -698,6 +712,12 @@ void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool w
     impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
 }
 
+void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 fill_value) {
+    if (!impl || fill_value != 0 || !impl->ClearBackingRegion(physical_offset, length)) {
+        std::memset(backing_base + physical_offset, fill_value, length);
+    }
+}
+
 void HostMemory::EnableDirectMappedAddress() {
     if (impl) {
         impl->EnableDirectMappedAddress();
diff --git a/src/common/host_memory.h b/src/common/host_memory.h
index cebfacab2..747c5850c 100644
--- a/src/common/host_memory.h
+++ b/src/common/host_memory.h
@@ -48,6 +48,8 @@ public:
 
     void EnableDirectMappedAddress();
 
+    void ClearBackingRegion(size_t physical_offset, size_t length, u32 fill_value);
+
     [[nodiscard]] u8* BackingBasePointer() noexcept {
         return backing_base;
     }
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index 0a973ec8c..d6bd27296 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -421,8 +421,9 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
     } else {
         // Set all the allocated memory.
         for (const auto& block : *out) {
-            std::memset(m_system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern,
-                        block.GetSize());
+            m_system.DeviceMemory().buffer.ClearBackingRegion(GetInteger(block.GetAddress()) -
+                                                                  Core::DramMemoryMap::Base,
+                                                              block.GetSize(), fill_pattern);
         }
     }
 
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
index 4c416d809..423289145 100644
--- a/src/core/hle/kernel/k_page_table_base.cpp
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -81,6 +81,11 @@ void InvalidateInstructionCache(KernelCore& kernel, AddressType addr, u64 size)
     }
 }
 
+void ClearBackingRegion(Core::System& system, KPhysicalAddress addr, u64 size, u32 fill_value) {
+    system.DeviceMemory().buffer.ClearBackingRegion(GetInteger(addr) - Core::DramMemoryMap::Base,
+                                                    size, fill_value);
+}
+
 template <typename AddressType>
 Result InvalidateDataCache(AddressType addr, u64 size) {
     R_SUCCEED();
@@ -1363,8 +1368,7 @@ Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) {
 
     // Clear all the newly allocated pages.
     for (const auto& it : pg) {
-        std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()),
-                    static_cast<u32>(m_heap_fill_value), it.GetSize());
+        ClearBackingRegion(m_system, it.GetAddress(), it.GetSize(), m_heap_fill_value);
     }
 
     // Lock the table.
@@ -1570,8 +1574,7 @@ Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProce
 
     // Clear all pages.
     for (const auto& it : pg) {
-        std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()),
-                    static_cast<u32>(m_heap_fill_value), it.GetSize());
+        ClearBackingRegion(m_system, it.GetAddress(), it.GetSize(), m_heap_fill_value);
     }
 
     // Map the pages.
@@ -2159,8 +2162,7 @@ Result KPageTableBase::SetHeapSize(KProcessAddress* out, size_t size) {
 
     // Clear all the newly allocated pages.
     for (const auto& it : pg) {
-        std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()), m_heap_fill_value,
-                    it.GetSize());
+        ClearBackingRegion(m_system, it.GetAddress(), it.GetSize(), m_heap_fill_value);
     }
 
     // Map the pages.