1 /* This file is part of the dynarmic project.
2  * Copyright (c) 2018 MerryMage
3  * SPDX-License-Identifier: 0BSD
4  */
5 
6 #pragma once
7 
8 #include <atomic>
9 #include <array>
10 #include <cstddef>
11 #include <cstdint>
12 #include <cstring>
13 #include <vector>
14 
15 namespace Dynarmic {
16 namespace A64 {
17 
18 using VAddr = std::uint64_t;
19 using Vector = std::array<std::uint64_t, 2>;
20 
21 class ExclusiveMonitor {
22 public:
23     /// @param processor_count Maximum number of processors using this global
24     ///                        exclusive monitor. Each processor must have a
25     ///                        unique id.
26     explicit ExclusiveMonitor(size_t processor_count);
27 
28     size_t GetProcessorCount() const;
29 
30     /// Marks a region containing [address, address+size) to be exclusive to
31     /// processor processor_id.
32     template <typename T, typename Function>
ReadAndMark(size_t processor_id,VAddr address,Function op)33     T ReadAndMark(size_t processor_id, VAddr address, Function op) {
34         static_assert(std::is_trivially_copyable_v<T>);
35         const VAddr masked_address = address & RESERVATION_GRANULE_MASK;
36 
37         Lock();
38         exclusive_addresses[processor_id] = masked_address;
39         const T value = op();
40         std::memcpy(exclusive_values[processor_id].data(), &value, sizeof(T));
41         Unlock();
42         return value;
43     }
44 
45     /// Checks to see if processor processor_id has exclusive access to the
46     /// specified region. If it does, executes the operation then clears
47     /// the exclusive state for processors if their exclusive region(s)
48     /// contain [address, address+size).
49     template <typename T, typename Function>
DoExclusiveOperation(size_t processor_id,VAddr address,Function op)50     bool DoExclusiveOperation(size_t processor_id, VAddr address, Function op) {
51         static_assert(std::is_trivially_copyable_v<T>);
52         if (!CheckAndClear(processor_id, address)) {
53             return false;
54         }
55 
56         T saved_value;
57         std::memcpy(&saved_value, exclusive_values[processor_id].data(), sizeof(T));
58         const bool result = op(saved_value);
59 
60         Unlock();
61         return result;
62     }
63 
64     /// Unmark everything.
65     void Clear();
66     /// Unmark processor id
67     void ClearProcessor(size_t processor_id);
68 
69 private:
70     bool CheckAndClear(size_t processor_id, VAddr address);
71 
72     void Lock();
73     void Unlock();
74 
75     static constexpr VAddr RESERVATION_GRANULE_MASK = 0xFFFF'FFFF'FFFF'FFFFull;
76     static constexpr VAddr INVALID_EXCLUSIVE_ADDRESS = 0xDEAD'DEAD'DEAD'DEADull;
77     std::atomic_flag is_locked;
78     std::vector<VAddr> exclusive_addresses;
79     std::vector<Vector> exclusive_values;
80 };
81 
82 } // namespace A64
83 } // namespace Dynarmic
84