From 0b1c2e5505c6478ef10e65c0b002eeb242e15540 Mon Sep 17 00:00:00 2001
From: MerryMage <MerryMage@users.noreply.github.com>
Date: Tue, 3 Jul 2018 14:28:46 +0100
Subject: [PATCH] Implement exclusive monitor

---
 src/core/CMakeLists.txt                |  2 +
 src/core/arm/dynarmic/arm_dynarmic.cpp | 67 +++++++++++++++++++++++---
 src/core/arm/dynarmic/arm_dynarmic.h   | 30 +++++++++++-
 src/core/arm/exclusive_monitor.cpp     |  7 +++
 src/core/arm/exclusive_monitor.h       | 23 +++++++++
 src/core/core.cpp                      |  3 +-
 src/core/core.h                        | 12 +++++
 src/core/core_cpu.cpp                  | 19 ++++++--
 src/core/core_cpu.h                    | 10 +++-
 9 files changed, 160 insertions(+), 13 deletions(-)
 create mode 100644 src/core/arm/exclusive_monitor.cpp
 create mode 100644 src/core/arm/exclusive_monitor.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 0a5d58eead..27a5de7fd0 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -1,5 +1,7 @@
 add_library(core STATIC
     arm/arm_interface.h
+    arm/exclusive_monitor.cpp
+    arm/exclusive_monitor.h
     arm/unicorn/arm_unicorn.cpp
     arm/unicorn/arm_unicorn.h
     core.cpp
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 5d7efc9b6a..83c09db2b6 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -102,18 +102,28 @@ public:
     u64 tpidr_el0 = 0;
 };
 
-std::unique_ptr<Dynarmic::A64::Jit> MakeJit(const std::unique_ptr<ARM_Dynarmic_Callbacks>& cb) {
+std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() {
     const auto page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data();
 
     Dynarmic::A64::UserConfig config;
+
+    // Callbacks
     config.callbacks = cb.get();
+
+    // Memory
+    config.page_table = reinterpret_cast<void**>(page_table);
+    config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
+    config.silently_mirror_page_table = false;
+
+    // Multi-process state
+    config.processor_id = core_index;
+    config.global_monitor = &exclusive_monitor->monitor;
+
+    // System registers
     config.tpidrro_el0 = &cb->tpidrro_el0;
     config.tpidr_el0 = &cb->tpidr_el0;
     config.dczid_el0 = 4;
     config.ctr_el0 = 0x8444c004;
-    config.page_table = reinterpret_cast<void**>(page_table);
-    config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
-    config.silently_mirror_page_table = false;
 
     return std::make_unique<Dynarmic::A64::Jit>(config);
 }
@@ -128,8 +138,11 @@ void ARM_Dynarmic::Step() {
     cb->InterpreterFallback(jit->GetPC(), 1);
 }
 
-ARM_Dynarmic::ARM_Dynarmic()
-    : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), jit(MakeJit(cb)) {
+ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index)
+    : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)),
+      jit(MakeJit()), exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(
+                          exclusive_monitor)},
+      core_index{core_index} {
     ARM_Interface::ThreadContext ctx;
     inner_unicorn.SaveContext(ctx);
     LoadContext(ctx);
@@ -237,6 +250,46 @@ void ARM_Dynarmic::ClearExclusiveState() {
 }
 
 void ARM_Dynarmic::PageTableChanged() {
-    jit = MakeJit(cb);
+    jit = MakeJit();
     current_page_table = Memory::GetCurrentPageTable();
 }
+
+DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(size_t core_count) : monitor(core_count) {}
+DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
+
+void DynarmicExclusiveMonitor::SetExclusive(size_t core_index, u64 addr) {
+    // Size doesn't actually matter.
+    monitor.Mark(core_index, addr, 16);
+}
+
+void DynarmicExclusiveMonitor::ClearExclusive() {
+    monitor.Clear();
+}
+
+bool DynarmicExclusiveMonitor::ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) {
+    return monitor.DoExclusiveOperation(core_index, vaddr, 1,
+                                        [&] { Memory::Write8(vaddr, value); });
+}
+
+bool DynarmicExclusiveMonitor::ExclusiveWrite16(size_t core_index, u64 vaddr, u16 value) {
+    return monitor.DoExclusiveOperation(core_index, vaddr, 2,
+                                        [&] { Memory::Write16(vaddr, value); });
+}
+
+bool DynarmicExclusiveMonitor::ExclusiveWrite32(size_t core_index, u64 vaddr, u32 value) {
+    return monitor.DoExclusiveOperation(core_index, vaddr, 4,
+                                        [&] { Memory::Write32(vaddr, value); });
+}
+
+bool DynarmicExclusiveMonitor::ExclusiveWrite64(size_t core_index, u64 vaddr, u64 value) {
+    return monitor.DoExclusiveOperation(core_index, vaddr, 8,
+                                        [&] { Memory::Write64(vaddr, value); });
+}
+
+bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, u64 vaddr,
+                                                 std::array<std::uint64_t, 2> value) {
+    return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
+        Memory::Write64(vaddr, value[0]);
+        Memory::Write64(vaddr, value[1]);
+    });
+}
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index a9891ac4fb..0fa8b417c4 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -6,15 +6,18 @@
 
 #include <memory>
 #include <dynarmic/A64/a64.h>
+#include <dynarmic/A64/exclusive_monitor.h>
 #include "common/common_types.h"
 #include "core/arm/arm_interface.h"
+#include "core/arm/exclusive_monitor.h"
 #include "core/arm/unicorn/arm_unicorn.h"
 
 class ARM_Dynarmic_Callbacks;
+class DynarmicExclusiveMonitor;
 
 class ARM_Dynarmic final : public ARM_Interface {
 public:
-    ARM_Dynarmic();
+    ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index);
     ~ARM_Dynarmic();
 
     void MapBackingMemory(VAddr address, size_t size, u8* memory,
@@ -47,10 +50,35 @@ public:
     void PageTableChanged() override;
 
 private:
+    std::unique_ptr<Dynarmic::A64::Jit> MakeJit();
+
     friend class ARM_Dynarmic_Callbacks;
     std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
     std::unique_ptr<Dynarmic::A64::Jit> jit;
     ARM_Unicorn inner_unicorn;
 
+    size_t core_index;
+    std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor;
+
     Memory::PageTable* current_page_table = nullptr;
 };
+
+class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
+public:
+    explicit DynarmicExclusiveMonitor(size_t core_count);
+    ~DynarmicExclusiveMonitor();
+
+    void SetExclusive(size_t core_index, u64 addr) override;
+    void ClearExclusive() override;
+
+    bool ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) override;
+    bool ExclusiveWrite16(size_t core_index, u64 vaddr, u16 value) override;
+    bool ExclusiveWrite32(size_t core_index, u64 vaddr, u32 value) override;
+    bool ExclusiveWrite64(size_t core_index, u64 vaddr, u64 value) override;
+    bool ExclusiveWrite128(size_t core_index, u64 vaddr,
+                           std::array<std::uint64_t, 2> value) override;
+
+private:
+    friend class ARM_Dynarmic;
+    Dynarmic::A64::ExclusiveMonitor monitor;
+};
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp
new file mode 100644
index 0000000000..cb8c81d800
--- /dev/null
+++ b/src/core/arm/exclusive_monitor.cpp
@@ -0,0 +1,7 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/arm/exclusive_monitor.h"
+
+ExclusiveMonitor::~ExclusiveMonitor() = default;
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h
new file mode 100644
index 0000000000..acfcdb94cc
--- /dev/null
+++ b/src/core/arm/exclusive_monitor.h
@@ -0,0 +1,23 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include "common/common_types.h"
+
+class ExclusiveMonitor {
+public:
+    virtual ~ExclusiveMonitor();
+
+    virtual void SetExclusive(size_t core_index, u64 addr) = 0;
+    virtual void ClearExclusive() = 0;
+
+    virtual bool ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) = 0;
+    virtual bool ExclusiveWrite16(size_t core_index, u64 vaddr, u16 value) = 0;
+    virtual bool ExclusiveWrite32(size_t core_index, u64 vaddr, u32 value) = 0;
+    virtual bool ExclusiveWrite64(size_t core_index, u64 vaddr, u64 value) = 0;
+    virtual bool ExclusiveWrite128(size_t core_index, u64 vaddr,
+                                   std::array<std::uint64_t, 2> value) = 0;
+};
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 9bd9f4bd95..b7f4b45323 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -171,8 +171,9 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
     current_process = Kernel::Process::Create("main");
 
     cpu_barrier = std::make_shared<CpuBarrier>();
+    cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
     for (size_t index = 0; index < cpu_cores.size(); ++index) {
-        cpu_cores[index] = std::make_shared<Cpu>(cpu_barrier, index);
+        cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
     }
 
     gpu_core = std::make_unique<Tegra::GPU>();
diff --git a/src/core/core.h b/src/core/core.h
index c6f69f001d..c123fe4018 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <thread>
 #include "common/common_types.h"
+#include "core/arm/exclusive_monitor.h"
 #include "core/core_cpu.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/scheduler.h"
@@ -114,6 +115,11 @@ public:
         return CurrentCpuCore().ArmInterface();
     }
 
+    /// Gets the index of the currently running CPU core
+    size_t CurrentCoreIndex() {
+        return CurrentCpuCore().CoreIndex();
+    }
+
     /// Gets an ARM interface to the CPU core with the specified index
     ARM_Interface& ArmInterface(size_t core_index);
 
@@ -130,6 +136,11 @@ public:
         return *CurrentCpuCore().Scheduler();
     }
 
+    /// Gets the exclusive monitor
+    ExclusiveMonitor& Monitor() {
+        return *cpu_exclusive_monitor;
+    }
+
     /// Gets the scheduler for the CPU core with the specified index
     const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index);
 
@@ -186,6 +197,7 @@ private:
     std::unique_ptr<Tegra::GPU> gpu_core;
     std::shared_ptr<Tegra::DebugContext> debug_context;
     Kernel::SharedPtr<Kernel::Process> current_process;
+    std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
     std::shared_ptr<CpuBarrier> cpu_barrier;
     std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
     std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index f22d6a9d06..54e15a7019 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -48,14 +48,15 @@ bool CpuBarrier::Rendezvous() {
     return false;
 }
 
-Cpu::Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
+Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
+         std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
     : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
 
     if (Settings::values.use_cpu_jit) {
 #ifdef ARCHITECTURE_x86_64
-        arm_interface = std::make_shared<ARM_Dynarmic>();
+        arm_interface = std::make_shared<ARM_Dynarmic>(exclusive_monitor, core_index);
 #else
-        cpu_core = std::make_shared<ARM_Unicorn>();
+        arm_interface = std::make_shared<ARM_Unicorn>();
         LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
 #endif
     } else {
@@ -65,6 +66,18 @@ Cpu::Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
     scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get());
 }
 
+std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(size_t num_cores) {
+    if (Settings::values.use_cpu_jit) {
+#ifdef ARCHITECTURE_x86_64
+        return std::make_shared<DynarmicExclusiveMonitor>(num_cores);
+#else
+        return nullptr; // TODO(merry): Passthrough exclusive monitor
+#endif
+    } else {
+        return nullptr; // TODO(merry): Passthrough exclusive monitor
+    }
+}
+
 void Cpu::RunLoop(bool tight_loop) {
     // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
     if (!cpu_barrier->Rendezvous()) {
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
index 243f0b5e74..9769529033 100644
--- a/src/core/core_cpu.h
+++ b/src/core/core_cpu.h
@@ -10,6 +10,7 @@
 #include <mutex>
 #include <string>
 #include "common/common_types.h"
+#include "core/arm/exclusive_monitor.h"
 
 class ARM_Interface;
 
@@ -40,7 +41,8 @@ private:
 
 class Cpu {
 public:
-    Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index);
+    Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
+        std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index);
 
     void RunLoop(bool tight_loop = true);
 
@@ -64,6 +66,12 @@ public:
         return core_index == 0;
     }
 
+    size_t CoreIndex() const {
+        return core_index;
+    }
+
+    static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(size_t num_cores);
+
 private:
     void Reschedule();
 
-- 
GitLab