summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp365
-rw-r--r--src/core/hle/kernel/k_address_arbiter.h70
3 files changed, 437 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index a870cd8fe..d29d4573e 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -157,6 +157,8 @@ add_library(core STATIC
157 hle/kernel/handle_table.h 157 hle/kernel/handle_table.h
158 hle/kernel/hle_ipc.cpp 158 hle/kernel/hle_ipc.cpp
159 hle/kernel/hle_ipc.h 159 hle/kernel/hle_ipc.h
160 hle/kernel/k_address_arbiter.cpp
161 hle/kernel/k_address_arbiter.h
160 hle/kernel/k_affinity_mask.h 162 hle/kernel/k_affinity_mask.h
161 hle/kernel/k_condition_variable.cpp 163 hle/kernel/k_condition_variable.cpp
162 hle/kernel/k_condition_variable.h 164 hle/kernel/k_condition_variable.h
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
new file mode 100644
index 000000000..7b712d31a
--- /dev/null
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -0,0 +1,365 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/arm/exclusive_monitor.h"
6#include "core/core.h"
7#include "core/hle/kernel/k_address_arbiter.h"
8#include "core/hle/kernel/k_scheduler.h"
9#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/svc_results.h"
12#include "core/hle/kernel/thread.h"
13#include "core/hle/kernel/time_manager.h"
14#include "core/memory.h"
15
16namespace Kernel {
17
18KAddressArbiter::KAddressArbiter(Core::System& system_)
19 : system{system_}, kernel{system.Kernel()} {}
20KAddressArbiter::~KAddressArbiter() = default;
21
22namespace {
23
24bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
25 *out = system.Memory().Read32(address);
26 return true;
27}
28
29bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
30 auto& monitor = system.Monitor();
31 const auto current_core = system.CurrentCoreIndex();
32
33 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
34 // TODO(bunnei): We should call CanAccessAtomic(..) here.
35
36 // Load the value from the address.
37 const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
38
39 // Compare it to the desired one.
40 if (current_value < value) {
41 // If less than, we want to try to decrement.
42 const s32 decrement_value = current_value - 1;
43
44 // Decrement and try to store.
45 if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value))) {
46 // If we failed to store, try again.
47 DecrementIfLessThan(system, out, address, value);
48 }
49 } else {
50 // Otherwise, clear our exclusive hold and finish
51 monitor.ClearExclusive();
52 }
53
54 // We're done.
55 *out = current_value;
56 return true;
57}
58
59bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
60 auto& monitor = system.Monitor();
61 const auto current_core = system.CurrentCoreIndex();
62
63 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
64 // TODO(bunnei): We should call CanAccessAtomic(..) here.
65
66 // Load the value from the address.
67 const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
68
69 // Compare it to the desired one.
70 if (current_value == value) {
71 // If equal, we want to try to write the new value.
72
73 // Try to store.
74 if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(new_value))) {
75 // If we failed to store, try again.
76 UpdateIfEqual(system, out, address, value, new_value);
77 }
78 } else {
79 // Otherwise, clear our exclusive hold and finish.
80 monitor.ClearExclusive();
81 }
82
83 // We're done.
84 *out = current_value;
85 return true;
86}
87
88} // namespace
89
90ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
91 // Perform signaling.
92 s32 num_waiters{};
93 {
94 KScopedSchedulerLock sl(kernel);
95
96 auto it = thread_tree.nfind_light({addr, -1});
97 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
98 (it->GetAddressArbiterKey() == addr)) {
99 Thread* target_thread = std::addressof(*it);
100 target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
101
102 ASSERT(target_thread->IsWaitingForAddressArbiter());
103 target_thread->Wakeup();
104
105 it = thread_tree.erase(it);
106 target_thread->ClearAddressArbiter();
107 ++num_waiters;
108 }
109 }
110 return RESULT_SUCCESS;
111}
112
113ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) {
114 // Perform signaling.
115 s32 num_waiters{};
116 {
117 KScopedSchedulerLock sl(kernel);
118
119 // Check the userspace value.
120 s32 user_value{};
121 R_UNLESS(UpdateIfEqual(system, std::addressof(user_value), addr, value, value + 1),
122 Svc::ResultInvalidCurrentMemory);
123 R_UNLESS(user_value == value, Svc::ResultInvalidState);
124
125 auto it = thread_tree.nfind_light({addr, -1});
126 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
127 (it->GetAddressArbiterKey() == addr)) {
128 Thread* target_thread = std::addressof(*it);
129 target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
130
131 ASSERT(target_thread->IsWaitingForAddressArbiter());
132 target_thread->Wakeup();
133
134 it = thread_tree.erase(it);
135 target_thread->ClearAddressArbiter();
136 ++num_waiters;
137 }
138 }
139 return RESULT_SUCCESS;
140}
141
142ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) {
143 // Perform signaling.
144 s32 num_waiters{};
145 {
146 KScopedSchedulerLock sl(kernel);
147
148 auto it = thread_tree.nfind_light({addr, -1});
149 // Determine the updated value.
150 s32 new_value{};
151 if (/*GetTargetFirmware() >= TargetFirmware_7_0_0*/ true) {
152 if (count <= 0) {
153 if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) {
154 new_value = value - 2;
155 } else {
156 new_value = value + 1;
157 }
158 } else {
159 if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) {
160 auto tmp_it = it;
161 s32 tmp_num_waiters{};
162 while ((++tmp_it != thread_tree.end()) &&
163 (tmp_it->GetAddressArbiterKey() == addr)) {
164 if ((tmp_num_waiters++) >= count) {
165 break;
166 }
167 }
168
169 if (tmp_num_waiters < count) {
170 new_value = value - 1;
171 } else {
172 new_value = value;
173 }
174 } else {
175 new_value = value + 1;
176 }
177 }
178 } else {
179 if (count <= 0) {
180 if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) {
181 new_value = value - 1;
182 } else {
183 new_value = value + 1;
184 }
185 } else {
186 auto tmp_it = it;
187 s32 tmp_num_waiters{};
188 while ((tmp_it != thread_tree.end()) && (tmp_it->GetAddressArbiterKey() == addr) &&
189 (tmp_num_waiters < count + 1)) {
190 ++tmp_num_waiters;
191 ++tmp_it;
192 }
193
194 if (tmp_num_waiters == 0) {
195 new_value = value + 1;
196 } else if (tmp_num_waiters <= count) {
197 new_value = value - 1;
198 } else {
199 new_value = value;
200 }
201 }
202 }
203
204 // Check the userspace value.
205 s32 user_value{};
206 bool succeeded{};
207 if (value != new_value) {
208 succeeded = UpdateIfEqual(system, std::addressof(user_value), addr, value, new_value);
209 } else {
210 succeeded = ReadFromUser(system, std::addressof(user_value), addr);
211 }
212
213 R_UNLESS(succeeded, Svc::ResultInvalidCurrentMemory);
214 R_UNLESS(user_value == value, Svc::ResultInvalidState);
215
216 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
217 (it->GetAddressArbiterKey() == addr)) {
218 Thread* target_thread = std::addressof(*it);
219 target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
220
221 ASSERT(target_thread->IsWaitingForAddressArbiter());
222 target_thread->Wakeup();
223
224 it = thread_tree.erase(it);
225 target_thread->ClearAddressArbiter();
226 ++num_waiters;
227 }
228 }
229 return RESULT_SUCCESS;
230}
231
232ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
233 // Prepare to wait.
234 Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
235 Handle timer = InvalidHandle;
236
237 {
238 KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout);
239
240 // Check that the thread isn't terminating.
241 if (cur_thread->IsTerminationRequested()) {
242 slp.CancelSleep();
243 return Svc::ResultTerminationRequested;
244 }
245
246 // Set the synced object.
247 cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
248
249 // Read the value from userspace.
250 s32 user_value{};
251 bool succeeded{};
252 if (decrement) {
253 succeeded = DecrementIfLessThan(system, std::addressof(user_value), addr, value);
254 } else {
255 succeeded = ReadFromUser(system, std::addressof(user_value), addr);
256 }
257
258 if (!succeeded) {
259 slp.CancelSleep();
260 return Svc::ResultInvalidCurrentMemory;
261 }
262
263 // Check that the value is less than the specified one.
264 if (user_value >= value) {
265 slp.CancelSleep();
266 return Svc::ResultInvalidState;
267 }
268
269 // Check that the timeout is non-zero.
270 if (timeout == 0) {
271 slp.CancelSleep();
272 return Svc::ResultTimedOut;
273 }
274
275 // Set the arbiter.
276 cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr);
277 thread_tree.insert(*cur_thread);
278 cur_thread->SetState(ThreadState::Waiting);
279 }
280
281 // Cancel the timer wait.
282 if (timer != InvalidHandle) {
283 auto& time_manager = kernel.TimeManager();
284 time_manager.UnscheduleTimeEvent(timer);
285 }
286
287 // Remove from the address arbiter.
288 {
289 KScopedSchedulerLock sl(kernel);
290
291 if (cur_thread->IsWaitingForAddressArbiter()) {
292 thread_tree.erase(thread_tree.iterator_to(*cur_thread));
293 cur_thread->ClearAddressArbiter();
294 }
295 }
296
297 // Get the result.
298 KSynchronizationObject* dummy{};
299 return cur_thread->GetWaitResult(std::addressof(dummy));
300}
301
302ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
303 // Prepare to wait.
304 Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
305 Handle timer = InvalidHandle;
306
307 {
308 KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout);
309
310 // Check that the thread isn't terminating.
311 if (cur_thread->IsTerminationRequested()) {
312 slp.CancelSleep();
313 return Svc::ResultTerminationRequested;
314 }
315
316 // Set the synced object.
317 cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
318
319 // Read the value from userspace.
320 s32 user_value{};
321 if (!ReadFromUser(system, std::addressof(user_value), addr)) {
322 slp.CancelSleep();
323 return Svc::ResultInvalidCurrentMemory;
324 }
325
326 // Check that the value is equal.
327 if (value != user_value) {
328 slp.CancelSleep();
329 return Svc::ResultInvalidState;
330 }
331
332 // Check that the timeout is non-zero.
333 if (timeout == 0) {
334 slp.CancelSleep();
335 return Svc::ResultTimedOut;
336 }
337
338 // Set the arbiter.
339 cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr);
340 thread_tree.insert(*cur_thread);
341 cur_thread->SetState(ThreadState::Waiting);
342 }
343
344 // Cancel the timer wait.
345 if (timer != InvalidHandle) {
346 auto& time_manager = kernel.TimeManager();
347 time_manager.UnscheduleTimeEvent(timer);
348 }
349
350 // Remove from the address arbiter.
351 {
352 KScopedSchedulerLock sl(kernel);
353
354 if (cur_thread->IsWaitingForAddressArbiter()) {
355 thread_tree.erase(thread_tree.iterator_to(*cur_thread));
356 cur_thread->ClearAddressArbiter();
357 }
358 }
359
360 // Get the result.
361 KSynchronizationObject* dummy{};
362 return cur_thread->GetWaitResult(std::addressof(dummy));
363}
364
365} // namespace Kernel
diff --git a/src/core/hle/kernel/k_address_arbiter.h b/src/core/hle/kernel/k_address_arbiter.h
new file mode 100644
index 000000000..8d379b524
--- /dev/null
+++ b/src/core/hle/kernel/k_address_arbiter.h
@@ -0,0 +1,70 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/assert.h"
8#include "common/common_types.h"
9#include "core/hle/kernel/k_condition_variable.h"
10#include "core/hle/kernel/svc_types.h"
11
12union ResultCode;
13
14namespace Core {
15class System;
16}
17
18namespace Kernel {
19
20class KernelCore;
21
22class KAddressArbiter {
23public:
24 using ThreadTree = KConditionVariable::ThreadTree;
25
26 explicit KAddressArbiter(Core::System& system_);
27 ~KAddressArbiter();
28
29 [[nodiscard]] ResultCode SignalToAddress(VAddr addr, Svc::SignalType type, s32 value,
30 s32 count) {
31 switch (type) {
32 case Svc::SignalType::Signal:
33 return Signal(addr, count);
34 case Svc::SignalType::SignalAndIncrementIfEqual:
35 return SignalAndIncrementIfEqual(addr, value, count);
36 case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
37 return SignalAndModifyByWaitingCountIfEqual(addr, value, count);
38 }
39 UNREACHABLE();
40 return RESULT_UNKNOWN;
41 }
42
43 [[nodiscard]] ResultCode WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value,
44 s64 timeout) {
45 switch (type) {
46 case Svc::ArbitrationType::WaitIfLessThan:
47 return WaitIfLessThan(addr, value, false, timeout);
48 case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
49 return WaitIfLessThan(addr, value, true, timeout);
50 case Svc::ArbitrationType::WaitIfEqual:
51 return WaitIfEqual(addr, value, timeout);
52 }
53 UNREACHABLE();
54 return RESULT_UNKNOWN;
55 }
56
57private:
58 [[nodiscard]] ResultCode Signal(VAddr addr, s32 count);
59 [[nodiscard]] ResultCode SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count);
60 [[nodiscard]] ResultCode SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count);
61 [[nodiscard]] ResultCode WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout);
62 [[nodiscard]] ResultCode WaitIfEqual(VAddr addr, s32 value, s64 timeout);
63
64 ThreadTree thread_tree;
65
66 Core::System& system;
67 KernelCore& kernel;
68};
69
70} // namespace Kernel