From 7f85abb28120fbb57bb813b828ee42f2a2031990 Mon Sep 17 00:00:00 2001
From: Markus Wick <markus@selfnet.de>
Date: Fri, 11 Jun 2021 11:47:23 +0200
Subject: [PATCH] common/host_memory: Implement a fallback if fastmem fails.

This falls back to the old approach of using a virtual buffer.

Windows is untested, but this build should fix support for Windows < 10 v1803. However without fastmem support at all.
---
 src/common/host_memory.cpp | 59 +++++++++++++++++++++++++++++---------
 src/common/host_memory.h   |  4 +++
 2 files changed, 49 insertions(+), 14 deletions(-)

diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index 9ae384f01c..8bd70abc79 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -449,21 +449,52 @@ private:
     int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
 };
 
-#else // ^^^ Linux ^^^
+#else // ^^^ Linux ^^^ vvv Generic vvv
 
-#error Please implement the host memory for your platform
+class HostMemory::Impl {
+public:
+    explicit Impl(size_t /*backing_size */, size_t /* virtual_size */) {
+        // This is just a place holder.
+        // Please implement fastmem in a propper way on your platform.
+        throw std::bad_alloc{};
+    }
 
-#endif
+    void Map(size_t virtual_offset, size_t host_offset, size_t length) {}
+
+    void Unmap(size_t virtual_offset, size_t length) {}
+
+    void Protect(size_t virtual_offset, size_t length, bool read, bool write) {}
+
+    u8* backing_base{nullptr};
+    u8* virtual_base{nullptr};
+};
+
+#endif // ^^^ Generic ^^^
 
 HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
-    : backing_size(backing_size_),
-      virtual_size(virtual_size_), impl{std::make_unique<HostMemory::Impl>(
-                                       AlignUp(backing_size, PageAlignment),
-                                       AlignUp(virtual_size, PageAlignment) + 3 * HugePageSize)},
-      backing_base{impl->backing_base}, virtual_base{impl->virtual_base} {
-    virtual_base += 2 * HugePageSize - 1;
-    virtual_base -= reinterpret_cast<size_t>(virtual_base) & (HugePageSize - 1);
-    virtual_base_offset = virtual_base - impl->virtual_base;
+    : backing_size(backing_size_), virtual_size(virtual_size_) {
+    try {
+        // Try to allocate a fastmem arena.
+        // The implementation will fail with std::bad_alloc on errors.
+        impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment),
+                                                  AlignUp(virtual_size, PageAlignment) +
+                                                      3 * HugePageSize);
+        backing_base = impl->backing_base;
+        virtual_base = impl->virtual_base;
+
+        if (virtual_base) {
+            virtual_base += 2 * HugePageSize - 1;
+            virtual_base -= reinterpret_cast<size_t>(virtual_base) & (HugePageSize - 1);
+            virtual_base_offset = virtual_base - impl->virtual_base;
+        }
+
+    } catch (const std::bad_alloc&) {
+        LOG_CRITICAL(HW_Memory,
+                     "Fastmem unavailable, falling back to VirtualBuffer for memory allocation");
+        fallback_buffer = std::make_unique<Common::VirtualBuffer<u8>>(backing_size);
+        backing_base = fallback_buffer->data();
+        virtual_base = nullptr;
+    }
 }
 
 HostMemory::~HostMemory() = default;
@@ -478,7 +509,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) {
     ASSERT(length % PageAlignment == 0);
     ASSERT(virtual_offset + length <= virtual_size);
     ASSERT(host_offset + length <= backing_size);
-    if (length == 0) {
+    if (length == 0 || !virtual_base || !impl) {
         return;
     }
     impl->Map(virtual_offset + virtual_base_offset, host_offset, length);
@@ -488,7 +519,7 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length) {
     ASSERT(virtual_offset % PageAlignment == 0);
     ASSERT(length % PageAlignment == 0);
     ASSERT(virtual_offset + length <= virtual_size);
-    if (length == 0) {
+    if (length == 0 || !virtual_base || !impl) {
         return;
     }
     impl->Unmap(virtual_offset + virtual_base_offset, length);
@@ -498,7 +529,7 @@ void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool w
     ASSERT(virtual_offset % PageAlignment == 0);
     ASSERT(length % PageAlignment == 0);
     ASSERT(virtual_offset + length <= virtual_size);
-    if (length == 0) {
+    if (length == 0 || !virtual_base || !impl) {
         return;
     }
     impl->Protect(virtual_offset + virtual_base_offset, length, read, write);
diff --git a/src/common/host_memory.h b/src/common/host_memory.h
index eaa7d18ab5..9b8326d0fb 100644
--- a/src/common/host_memory.h
+++ b/src/common/host_memory.h
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include "common/common_types.h"
+#include "common/virtual_buffer.h"
 
 namespace Common {
 
@@ -61,6 +62,9 @@ private:
     u8* backing_base{};
     u8* virtual_base{};
     size_t virtual_base_offset{};
+
+    // Fallback if fastmem is not supported on this platform
+    std::unique_ptr<Common::VirtualBuffer<u8>> fallback_buffer;
 };
 
 } // namespace Common