From 6072b22a0b9a1d84c389e8231fe4b6a97e60d55f Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Thu, 1 Dec 2022 22:48:43 -0500
Subject: [PATCH] core: add option to break on unmapped access

---
 src/common/settings.h                         |  1 +
 src/core/arm/arm_interface.cpp                |  8 +++++--
 src/core/arm/dynarmic/arm_dynarmic_32.cpp     | 21 +++++++++++++++++--
 src/core/arm/dynarmic/arm_dynarmic_64.cpp     | 21 +++++++++++++++++--
 src/yuzu/configuration/config.cpp             |  2 ++
 .../configuration/configure_cpu_debug.cpp     |  4 ++++
 src/yuzu/configuration/configure_cpu_debug.ui | 13 ++++++++++++
 src/yuzu_cmd/config.cpp                       |  1 +
 src/yuzu_cmd/default_ini.h                    |  4 ++++
 9 files changed, 69 insertions(+), 6 deletions(-)

diff --git a/src/common/settings.h b/src/common/settings.h
index 00e4421f7..c29d6f98b 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -399,6 +399,7 @@ struct Values {
     Setting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"};
     Setting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"};
     Setting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"};
+    Setting<bool> cpuopt_ignore_memory_aborts{true, "cpuopt_ignore_memory_aborts"};
 
     SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"};
     SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"};
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 29ba562dc..2df7b0ee8 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -145,11 +145,15 @@ void ARM_Interface::Run() {
         // Notify the debugger and go to sleep if a breakpoint was hit,
         // or if the thread is unable to continue for any reason.
         if (Has(hr, breakpoint) || Has(hr, no_execute)) {
-            RewindBreakpointInstruction();
+            if (!Has(hr, no_execute)) {
+                RewindBreakpointInstruction();
+            }
             if (system.DebuggerEnabled()) {
                 system.GetDebugger().NotifyThreadStopped(current_thread);
+            } else {
+                LogBacktrace();
             }
-            current_thread->RequestSuspend(Kernel::SuspendType::Debug);
+            current_thread->RequestSuspend(SuspendType::Debug);
             break;
         }
 
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 227e06ea1..947747d36 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -29,7 +29,9 @@ class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
 public:
     explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
         : parent{parent_},
-          memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
+          memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()},
+          check_memory_access{debugger_enabled ||
+                              !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {}
 
     u8 MemoryRead8(u32 vaddr) override {
         CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
@@ -154,6 +156,17 @@ public:
     }
 
     bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
+        if (!check_memory_access) {
+            return true;
+        }
+
+        if (!memory.IsValidVirtualAddressRange(addr, size)) {
+            LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}",
+                         addr);
+            parent.jit.load()->HaltExecution(ARM_Interface::no_execute);
+            return false;
+        }
+
         if (!debugger_enabled) {
             return true;
         }
@@ -181,7 +194,8 @@ public:
     ARM_Dynarmic_32& parent;
     Core::Memory::Memory& memory;
     std::size_t num_interpreted_instructions{};
-    bool debugger_enabled{};
+    const bool debugger_enabled{};
+    const bool check_memory_access{};
     static constexpr u64 minimum_run_cycles = 10000U;
 };
 
@@ -264,6 +278,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
         if (!Settings::values.cpuopt_recompile_exclusives) {
             config.recompile_on_exclusive_fastmem_failure = false;
         }
+        if (!Settings::values.cpuopt_ignore_memory_aborts) {
+            config.check_halt_on_memory_access = true;
+        }
     } else {
         // Unsafe optimizations
         if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index cb53d64ba..3df943df7 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -29,7 +29,9 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
 public:
     explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
         : parent{parent_},
-          memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
+          memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()},
+          check_memory_access{debugger_enabled ||
+                              !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {}
 
     u8 MemoryRead8(u64 vaddr) override {
         CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
@@ -198,6 +200,17 @@ public:
     }
 
     bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
+        if (!check_memory_access) {
+            return true;
+        }
+
+        if (!memory.IsValidVirtualAddressRange(addr, size)) {
+            LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}",
+                         addr);
+            parent.jit.load()->HaltExecution(ARM_Interface::no_execute);
+            return false;
+        }
+
         if (!debugger_enabled) {
             return true;
         }
@@ -226,7 +239,8 @@ public:
     Core::Memory::Memory& memory;
     u64 tpidrro_el0 = 0;
     u64 tpidr_el0 = 0;
-    bool debugger_enabled{};
+    const bool debugger_enabled{};
+    const bool check_memory_access{};
     static constexpr u64 minimum_run_cycles = 10000U;
 };
 
@@ -323,6 +337,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
         if (!Settings::values.cpuopt_recompile_exclusives) {
             config.recompile_on_exclusive_fastmem_failure = false;
         }
+        if (!Settings::values.cpuopt_ignore_memory_aborts) {
+            config.check_halt_on_memory_access = true;
+        }
     } else {
         // Unsafe optimizations
         if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 0c93df428..750285478 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -658,6 +658,7 @@ void Config::ReadCpuValues() {
         ReadBasicSetting(Settings::values.cpuopt_fastmem);
         ReadBasicSetting(Settings::values.cpuopt_fastmem_exclusives);
         ReadBasicSetting(Settings::values.cpuopt_recompile_exclusives);
+        ReadBasicSetting(Settings::values.cpuopt_ignore_memory_aborts);
     }
 
     qt_config->endGroup();
@@ -1257,6 +1258,7 @@ void Config::SaveCpuValues() {
         WriteBasicSetting(Settings::values.cpuopt_fastmem);
         WriteBasicSetting(Settings::values.cpuopt_fastmem_exclusives);
         WriteBasicSetting(Settings::values.cpuopt_recompile_exclusives);
+        WriteBasicSetting(Settings::values.cpuopt_ignore_memory_aborts);
     }
 
     qt_config->endGroup();
diff --git a/src/yuzu/configuration/configure_cpu_debug.cpp b/src/yuzu/configuration/configure_cpu_debug.cpp
index 3c302ec16..8cfef0cc1 100644
--- a/src/yuzu/configuration/configure_cpu_debug.cpp
+++ b/src/yuzu/configuration/configure_cpu_debug.cpp
@@ -45,6 +45,9 @@ void ConfigureCpuDebug::SetConfiguration() {
     ui->cpuopt_recompile_exclusives->setEnabled(runtime_lock);
     ui->cpuopt_recompile_exclusives->setChecked(
         Settings::values.cpuopt_recompile_exclusives.GetValue());
+    ui->cpuopt_ignore_memory_aborts->setEnabled(runtime_lock);
+    ui->cpuopt_ignore_memory_aborts->setChecked(
+        Settings::values.cpuopt_ignore_memory_aborts.GetValue());
 }
 
 void ConfigureCpuDebug::ApplyConfiguration() {
@@ -59,6 +62,7 @@ void ConfigureCpuDebug::ApplyConfiguration() {
     Settings::values.cpuopt_fastmem = ui->cpuopt_fastmem->isChecked();
     Settings::values.cpuopt_fastmem_exclusives = ui->cpuopt_fastmem_exclusives->isChecked();
     Settings::values.cpuopt_recompile_exclusives = ui->cpuopt_recompile_exclusives->isChecked();
+    Settings::values.cpuopt_ignore_memory_aborts = ui->cpuopt_ignore_memory_aborts->isChecked();
 }
 
 void ConfigureCpuDebug::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_cpu_debug.ui b/src/yuzu/configuration/configure_cpu_debug.ui
index 2bc268810..3010f7fad 100644
--- a/src/yuzu/configuration/configure_cpu_debug.ui
+++ b/src/yuzu/configuration/configure_cpu_debug.ui
@@ -175,6 +175,19 @@
           </property>
          </widget>
         </item>
+        <item>
+         <widget class="QCheckBox" name="cpuopt_ignore_memory_aborts">
+          <property name="toolTip">
+           <string>
+            &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by allowing invalid memory accesses to succeed.&lt;/div&gt;
+            &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it reduces the overhead of all memory accesses and has no impact on programs that don't access invalid memory.&lt;/div&gt;
+           </string>
+          </property>
+          <property name="text">
+           <string>Enable fallbacks for invalid memory accesses</string>
+          </property>
+         </widget>
+        </item>
        </layout>
       </widget>
      </item>
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 59f9c8e09..767b0d0e3 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -286,6 +286,7 @@ void Config::ReadValues() {
     ReadSetting("Cpu", Settings::values.cpuopt_fastmem);
     ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives);
     ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives);
+    ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts);
     ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma);
     ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error);
     ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 5bbc3f532..6fcf04e1b 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -208,6 +208,10 @@ cpuopt_fastmem_exclusives =
 # 0: Disabled, 1 (default): Enabled
 cpuopt_recompile_exclusives =
 
+# Enable optimization to ignore invalid memory accesses (faster guest memory access)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_ignore_memory_aborts =
+
 # Enable unfuse FMA (improve performance on CPUs without FMA)
 # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
 # 0: Disabled, 1 (default): Enabled