From 7e191dccc184ae85ce5ade2bca913ab331002481 Mon Sep 17 00:00:00 2001
From: Michael Scire <SciresM@gmail.com>
Date: Thu, 21 Jun 2018 00:49:43 -0600
Subject: [PATCH] Kernel/Arbiters: Add stubs for 4.x
 SignalToAddress/WaitForAddres SVCs.

---
 src/core/hle/kernel/address_arbiter.cpp | 46 +++++++++++++++++++++
 src/core/hle/kernel/address_arbiter.h   | 32 +++++++++++++++
 src/core/hle/kernel/errors.h            | 10 +++--
 src/core/hle/kernel/mutex.cpp           |  4 +-
 src/core/hle/kernel/svc.cpp             | 53 +++++++++++++++++++++++--
 src/core/hle/kernel/svc_wrap.h          | 10 +++++
 src/core/hle/kernel/thread.h            |  1 +
 7 files changed, 147 insertions(+), 9 deletions(-)
 create mode 100644 src/core/hle/kernel/address_arbiter.cpp
 create mode 100644 src/core/hle/kernel/address_arbiter.h

diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
new file mode 100644
index 000000000..cfd2c1590
--- /dev/null
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -0,0 +1,46 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/thread.h"
+#include "core/memory.h"
+
+namespace Kernel {
+    namespace AddressArbiter {
+
+        // Signals an address being waited on.
+        ResultCode SignalToAddress(VAddr address, s32 value, s32 num_to_wake) {
+            // TODO
+            return RESULT_SUCCESS;
+        }
+
+        // Signals an address being waited on and increments its value if equal to the value argument.
+        ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) {
+            // TODO
+            return RESULT_SUCCESS;
+        }
+
+        // Signals an address being waited on and modifies its value based on waiting thread count if equal to the value argument.
+        ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) {
+            // TODO
+            return RESULT_SUCCESS;
+        }
+
+        // Waits on an address if the value passed is less than the argument value, optionally decrementing.
+        ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) {
+            // TODO
+            return RESULT_SUCCESS;
+        }
+
+        // Waits on an address if the value passed is equal to the argument value.
+        ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
+            // TODO
+            return RESULT_SUCCESS;
+        }
+    } // namespace AddressArbiter
+} // namespace Kernel
\ No newline at end of file
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
new file mode 100644
index 000000000..3eba103dd
--- /dev/null
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -0,0 +1,32 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+    namespace AddressArbiter {
+        enum class ArbitrationType {
+            WaitIfLessThan = 0,
+            DecrementAndWaitIfLessThan = 1,
+            WaitIfEqual = 2,
+        };
+
+        enum class SignalType {
+            Signal = 0,
+            IncrementAndSignalIfEqual = 1,
+            ModifyByWaitingCountAndSignalIfEqual = 2,
+        };
+
+        ResultCode SignalToAddress(VAddr address, s32 value, s32 num_to_wake);
+        ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
+        ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
+
+        ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement);
+        ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
+    } // namespace AddressArbiter
+
+} // namespace Kernel
\ No newline at end of file
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index e1b5430bf..7ac960042 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -20,13 +20,15 @@ enum {
     MaxConnectionsReached = 52,
 
     // Confirmed Switch OS error codes
-    MisalignedAddress = 102,
+    InvalidAddress = 102,
+    InvalidMemoryState = 106,
     InvalidProcessorId = 113,
     InvalidHandle = 114,
     InvalidCombination = 116,
     Timeout = 117,
     SynchronizationCanceled = 118,
     TooLarge = 119,
+    InvalidEnumValue = 120,
 };
 }
 
@@ -39,13 +41,13 @@ constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1);
 constexpr ResultCode ERR_PORT_NAME_TOO_LONG(-1);
 constexpr ResultCode ERR_WRONG_PERMISSION(-1);
 constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1);
-constexpr ResultCode ERR_INVALID_ENUM_VALUE(-1);
+constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
 constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1);
 constexpr ResultCode ERR_INVALID_COMBINATION(-1);
 constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(-1);
 constexpr ResultCode ERR_OUT_OF_MEMORY(-1);
-constexpr ResultCode ERR_INVALID_ADDRESS(-1);
-constexpr ResultCode ERR_INVALID_ADDRESS_STATE(-1);
+constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
+constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
 constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
 constexpr ResultCode ERR_INVALID_POINTER(-1);
 constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1);
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index bc144f3de..65560226d 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -59,7 +59,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
                              Handle requesting_thread_handle) {
     // The mutex address must be 4-byte aligned
     if ((address % sizeof(u32)) != 0) {
-        return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
+        return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress);
     }
 
     SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle);
@@ -97,7 +97,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
 ResultCode Mutex::Release(VAddr address) {
     // The mutex address must be 4-byte aligned
     if ((address % sizeof(u32)) != 0) {
-        return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
+        return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress);
     }
 
     auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address);
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 69ba7b777..4b4831c08 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -11,6 +11,7 @@
 #include "common/string_util.h"
 #include "core/core.h"
 #include "core/core_timing.h"
+#include "core/hle/kernel/address_arbiter.h"
 #include "core/hle/kernel/client_port.h"
 #include "core/hle/kernel/client_session.h"
 #include "core/hle/kernel/event.h"
@@ -580,7 +581,7 @@ static void SleepThread(s64 nanoseconds) {
     Core::System::GetInstance().PrepareReschedule();
 }
 
-/// Signal process wide key atomic
+/// Wait process wide key atomic
 static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_variable_addr,
                                            Handle thread_handle, s64 nano_seconds) {
     NGLOG_TRACE(
@@ -689,6 +690,52 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
     return RESULT_SUCCESS;
 }
 
+// Wait for an address (via Address Arbiter)
+static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout) {
+    // If the passed address is a kernel virtual address, return invalid memory state.
+    if ((address + 0x8000000000LL) < 0x7FFFE00000LL) {
+        return ERR_INVALID_ADDRESS_STATE;
+    }
+    // If the address is not properly aligned to 4 bytes, return invalid address.
+    if (address % sizeof(u32) != 0) {
+        return ERR_INVALID_ADDRESS;
+    }
+
+    switch ((AddressArbiter::ArbitrationType)type) {
+        case AddressArbiter::ArbitrationType::WaitIfLessThan:
+            return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, false);
+        case AddressArbiter::ArbitrationType::DecrementAndWaitIfLessThan:
+            return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, true);
+        case AddressArbiter::ArbitrationType::WaitIfEqual:
+            return AddressArbiter::WaitForAddressIfEqual(address, value, timeout);
+        default:
+            return ERR_INVALID_ENUM_VALUE;
+    }
+}
+
+// Signals to an address (via Address Arbiter)
+static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to_wake) {
+    // If the passed address is a kernel virtual address, return invalid memory state.
+    if ((address + 0x8000000000LL) < 0x7FFFE00000LL) {
+        return ERR_INVALID_ADDRESS_STATE;
+    }
+    // If the address is not properly aligned to 4 bytes, return invalid address.
+    if (address % sizeof(u32) != 0) {
+        return ERR_INVALID_ADDRESS;
+    }
+
+    switch ((AddressArbiter::SignalType)type) {
+        case AddressArbiter::SignalType::Signal:
+            return AddressArbiter::SignalToAddress(address, value, num_to_wake);
+        case AddressArbiter::SignalType::IncrementAndSignalIfEqual:
+            return AddressArbiter::IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
+        case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual:
+            return AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, num_to_wake);
+        default:
+            return ERR_INVALID_ENUM_VALUE;
+    }
+}
+
 /// This returns the total CPU ticks elapsed since the CPU was powered-on
 static u64 GetSystemTick() {
     const u64 result{CoreTiming::GetTicks()};
@@ -861,8 +908,8 @@ static const FunctionDef SVC_Table[] = {
     {0x31, nullptr, "GetResourceLimitCurrentValue"},
     {0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"},
     {0x33, SvcWrap<GetThreadContext>, "GetThreadContext"},
-    {0x34, nullptr, "WaitForAddress"},
-    {0x35, nullptr, "SignalToAddress"},
+    {0x34, SvcWrap<WaitForAddress>, "WaitForAddress"},
+    {0x35, SvcWrap<SignalToAddress>, "SignalToAddress"},
     {0x36, nullptr, "Unknown"},
     {0x37, nullptr, "Unknown"},
     {0x38, nullptr, "Unknown"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 40aa88cc1..efae4fdfe 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -179,6 +179,16 @@ void SvcWrap() {
     FuncReturn(retval);
 }
 
+template <ResultCode func(u64, u32, s32, s64)>
+void SvcWrap() {
+    FuncReturn(func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3)).raw);
+}
+
+template <ResultCode func(u64, u32, s32, s32)>
+void SvcWrap() {
+    FuncReturn(func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), (s32)(PARAM(3) & 0xFFFFFFFF)).raw);
+}
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 // Function wrappers that return type u32
 
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 1d2da6d50..023c9dbe9 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -45,6 +45,7 @@ enum ThreadStatus {
     THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
     THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
     THREADSTATUS_WAIT_MUTEX,     ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc
+    THREADSTATUS_WAIT_ARB,       ///< Waiting due to a SignalToAddress/WaitForAddress svc
     THREADSTATUS_DORMANT,        ///< Created but not yet made ready
     THREADSTATUS_DEAD            ///< Run to completion, or forcefully terminated
 };