From 7e191dccc184ae85ce5ade2bca913ab331002481 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 21 Jun 2018 00:49:43 -0600 Subject: 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 (limited to 'src') 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 holding_thread = g_handle_table.Get(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"}, {0x33, SvcWrap, "GetThreadContext"}, - {0x34, nullptr, "WaitForAddress"}, - {0x35, nullptr, "SignalToAddress"}, + {0x34, SvcWrap, "WaitForAddress"}, + {0x35, SvcWrap, "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 +void SvcWrap() { + FuncReturn(func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3)).raw); +} + +template +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 }; -- cgit v1.2.3 From 9d71ce88cee58d2e171ec5ed82daf075112fb422 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 21 Jun 2018 01:40:29 -0600 Subject: Kernel/Arbiters: Implement WaitForAddress --- src/core/hle/kernel/address_arbiter.cpp | 59 ++++++++++++++++++++++++++++++--- src/core/hle/kernel/errors.h | 2 ++ src/core/hle/kernel/thread.cpp | 6 ++++ src/core/hle/kernel/thread.h | 6 ++-- src/yuzu/debugger/wait_tree.cpp | 4 +++ 5 files changed, 71 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index cfd2c1590..367c4520d 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -5,14 +5,31 @@ #include "common/assert.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "core/core.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" +#include "core/hle/lock.h" #include "core/memory.h" namespace Kernel { namespace AddressArbiter { + // Performs actual address waiting logic. + ResultCode WaitForAddress(VAddr address, s64 timeout) { + SharedPtr current_thread = GetCurrentThread(); + current_thread->arb_wait_address = address; + current_thread->arb_wait_result = RESULT_TIMEOUT; + current_thread->status = THREADSTATUS_WAIT_ARB; + current_thread->wakeup_callback = nullptr; + + current_thread->WakeAfterDelay(timeout); + + Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); + return RESULT_SUCCESS; + } + // Signals an address being waited on. ResultCode SignalToAddress(VAddr address, s32 value, s32 num_to_wake) { // TODO @@ -33,14 +50,48 @@ namespace Kernel { // 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; + // Ensure that we can read the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + + s32 cur_value; + // Get value, decrementing if less than + { + // Decrement if less than must be an atomic operation. + std::lock_guard lock(HLE::g_hle_lock); + cur_value = (s32)Memory::Read32(address); + if (cur_value < value) { + Memory::Write32(address, (u32)(cur_value - 1)); + } + } + if (cur_value >= value) { + return ERR_INVALID_STATE; + } + // Short-circuit without rescheduling, if timeout is zero. + if (timeout == 0) { + return RESULT_TIMEOUT; + } + + return WaitForAddress(address, timeout); } // 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; + // Ensure that we can read the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + // Only wait for the address if equal. + if ((s32)Memory::Read32(address) != value) { + return ERR_INVALID_STATE; + } + // Short-circuit without rescheduling, if timeout is zero. + if (timeout == 0) { + return RESULT_TIMEOUT; + } + + return WaitForAddress(address, 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 7ac960042..221cb1bb5 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -29,6 +29,7 @@ enum { SynchronizationCanceled = 118, TooLarge = 119, InvalidEnumValue = 120, + InvalidState = 125, }; } @@ -49,6 +50,7 @@ constexpr ResultCode ERR_OUT_OF_MEMORY(-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_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); constexpr ResultCode ERR_INVALID_POINTER(-1); constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1); constexpr ResultCode ERR_NOT_AUTHORIZED(-1); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index cffa7ca83..2f333ec34 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -140,6 +140,11 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { } } + if (thread->arb_wait_address != 0) { + ASSERT(thread->status == THREADSTATUS_WAIT_ARB); + thread->arb_wait_address = 0; + } + if (resume) thread->ResumeFromWait(); } @@ -179,6 +184,7 @@ void Thread::ResumeFromWait() { case THREADSTATUS_WAIT_SLEEP: case THREADSTATUS_WAIT_IPC: case THREADSTATUS_WAIT_MUTEX: + case THREADSTATUS_WAIT_ARB: break; case THREADSTATUS_READY: diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 023c9dbe9..79e5d6e5c 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -228,8 +228,10 @@ public: // If waiting on a ConditionVariable, this is the ConditionVariable address VAddr condvar_wait_address; - VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address - Handle wait_handle; ///< The handle used to wait for the mutex. + VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address + Handle wait_handle; ///< The handle used to wait for the mutex. + VAddr arb_wait_address; ///< If waiting for an AddressArbiter, this is the address + ResultCode arb_wait_result; ///< If waiting for an AddressArbiter, this is the result that will be returned. std::string name; diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 017bef13c..800e431bd 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -213,6 +213,9 @@ QString WaitTreeThread::GetText() const { case THREADSTATUS_WAIT_MUTEX: status = tr("waiting for mutex"); break; + case THREADSTATUS_WAIT_MUTEX: + status = tr("waiting for address arbiter"); + break; case THREADSTATUS_DORMANT: status = tr("dormant"); break; @@ -240,6 +243,7 @@ QColor WaitTreeThread::GetColor() const { case THREADSTATUS_WAIT_SYNCH_ALL: case THREADSTATUS_WAIT_SYNCH_ANY: case THREADSTATUS_WAIT_MUTEX: + case THREADSTATUS_WAIT_ARB: return QColor(Qt::GlobalColor::red); case THREADSTATUS_DORMANT: return QColor(Qt::GlobalColor::darkCyan); -- cgit v1.2.3 From 4f81bc4e1bd12e4df7410c6790ba818d8dbba9c0 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 21 Jun 2018 04:07:03 -0600 Subject: Kernel/Arbiters: Mostly implement SignalToAddress --- src/core/hle/kernel/address_arbiter.cpp | 110 ++++++++++++++++++++++++++++++-- src/core/hle/kernel/address_arbiter.h | 2 +- src/core/hle/kernel/svc.cpp | 2 +- src/core/hle/kernel/thread.h | 6 +- src/yuzu/debugger/wait_tree.cpp | 2 +- 5 files changed, 111 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 367c4520d..4556199ab 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -27,24 +27,122 @@ namespace Kernel { current_thread->WakeAfterDelay(timeout); Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); - return RESULT_SUCCESS; + return current_thread->arb_wait_result; + } + + // Gets the threads waiting on an address. + void GetThreadsWaitingOnAddress(std::vector> &waiting_threads, VAddr address) { + auto RetrieveWaitingThreads = + [](size_t core_index, std::vector>& waiting_threads, VAddr arb_addr) { + const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); + auto& thread_list = scheduler->GetThreadList(); + + for (auto& thread : thread_list) { + if (thread->arb_wait_address == arb_addr) + waiting_threads.push_back(thread); + } + }; + + // Retrieve a list of all threads that are waiting for this address. + RetrieveWaitingThreads(0, waiting_threads, address); + RetrieveWaitingThreads(1, waiting_threads, address); + RetrieveWaitingThreads(2, waiting_threads, address); + RetrieveWaitingThreads(3, waiting_threads, address); + // Sort them by priority, such that the highest priority ones come first. + std::sort(waiting_threads.begin(), waiting_threads.end(), + [](const SharedPtr& lhs, const SharedPtr& rhs) { + return lhs->current_priority < rhs->current_priority; + }); + } + + // Wake up num_to_wake (or all) threads in a vector. + void WakeThreads(std::vector> &waiting_threads, s32 num_to_wake) { + // Only process up to 'target' threads, unless 'target' is <= 0, in which case process + // them all. + size_t last = waiting_threads.size(); + if (num_to_wake > 0) + last = num_to_wake; + + // Signal the waiting threads. + // TODO: Rescheduling should not occur while waking threads. How can it be prevented? + for (size_t i = 0; i < last; i++) { + ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB); + waiting_threads[i]->arb_wait_result = RESULT_SUCCESS; + waiting_threads[i]->ResumeFromWait(); + } + } // Signals an address being waited on. - ResultCode SignalToAddress(VAddr address, s32 value, s32 num_to_wake) { - // TODO + ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { + // Get threads waiting on the address. + std::vector> waiting_threads; + GetThreadsWaitingOnAddress(waiting_threads, address); + + WakeThreads(waiting_threads, num_to_wake); 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; + // Ensure that we can write to the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + + s32 cur_value; + // Get value, incrementing if equal. + { + // Increment if Equal must be an atomic operation. + std::lock_guard lock(HLE::g_hle_lock); + cur_value = (s32)Memory::Read32(address); + if (cur_value == value) { + Memory::Write32(address, (u32)(cur_value + 1)); + } + } + if (cur_value != value) { + return ERR_INVALID_STATE; + } + + return SignalToAddress(address, num_to_wake); } // 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 + // Ensure that we can write to the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + + // Get threads waiting on the address. + std::vector> waiting_threads; + GetThreadsWaitingOnAddress(waiting_threads, address); + + // Determine the modified value depending on the waiting count. + s32 updated_value; + if (waiting_threads.size() == 0) { + updated_value = value - 1; + } else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) { + updated_value = value + 1; + } else { + updated_value = value; + } + s32 cur_value; + // Perform an atomic update if equal. + { + std::lock_guard lock(HLE::g_hle_lock); + cur_value = (s32)Memory::Read32(address); + if (cur_value == value) { + Memory::Write32(address, (u32)(updated_value)); + } + } + + // Only continue if equal. + if (cur_value != value) { + return ERR_INVALID_STATE; + } + + WakeThreads(waiting_threads, num_to_wake); return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 3eba103dd..32d4a77a9 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -21,7 +21,7 @@ namespace Kernel { ModifyByWaitingCountAndSignalIfEqual = 2, }; - ResultCode SignalToAddress(VAddr address, s32 value, s32 num_to_wake); + ResultCode SignalToAddress(VAddr address, s32 num_to_wake); ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4b4831c08..24dd50938 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -726,7 +726,7 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to switch ((AddressArbiter::SignalType)type) { case AddressArbiter::SignalType::Signal: - return AddressArbiter::SignalToAddress(address, value, num_to_wake); + return AddressArbiter::SignalToAddress(address, num_to_wake); case AddressArbiter::SignalType::IncrementAndSignalIfEqual: return AddressArbiter::IncrementAndSignalToAddressIfEqual(address, value, num_to_wake); case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual: diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 79e5d6e5c..0a76bd222 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -230,8 +230,10 @@ public: VAddr condvar_wait_address; VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address Handle wait_handle; ///< The handle used to wait for the mutex. - VAddr arb_wait_address; ///< If waiting for an AddressArbiter, this is the address - ResultCode arb_wait_result; ///< If waiting for an AddressArbiter, this is the result that will be returned. + + // If waiting for an AddressArbiter, this is the address being waited on. + VAddr arb_wait_address; + ResultCode arb_wait_result{RESULT_SUCCESS}; ///< Result returned when done waiting on AddressArbiter. std::string name; diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 800e431bd..7101b381e 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -213,7 +213,7 @@ QString WaitTreeThread::GetText() const { case THREADSTATUS_WAIT_MUTEX: status = tr("waiting for mutex"); break; - case THREADSTATUS_WAIT_MUTEX: + case THREADSTATUS_WAIT_ARB: status = tr("waiting for address arbiter"); break; case THREADSTATUS_DORMANT: -- cgit v1.2.3 From 62bd1299ea8c855b4951a15f9f3b645c2953ee0c Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 21 Jun 2018 04:20:39 -0600 Subject: Kernel/Arbiters: Clear WaitAddress in SignalToAddress --- src/core/hle/kernel/address_arbiter.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 4556199ab..63cdcb559 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -68,6 +68,7 @@ namespace Kernel { for (size_t i = 0; i < last; i++) { ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB); waiting_threads[i]->arb_wait_result = RESULT_SUCCESS; + waiting_threads[i]->arb_wait_address = 0; waiting_threads[i]->ResumeFromWait(); } -- cgit v1.2.3 From 8f8fe62a19060a7c4529c4e0870412e9cd97e841 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 21 Jun 2018 05:13:06 -0600 Subject: Kernel/Arbiters: Initialize arb_wait_address in thread struct. --- src/core/CMakeLists.txt | 2 ++ src/core/hle/kernel/svc.cpp | 4 ++++ src/core/hle/kernel/thread.h | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ba5b02174..051eda98d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -40,6 +40,8 @@ add_library(core STATIC hle/config_mem.h hle/ipc.h hle/ipc_helpers.h + hle/kernel/address_arbiter.cpp + hle/kernel/address_arbiter.h hle/kernel/client_port.cpp hle/kernel/client_port.h hle/kernel/client_session.cpp diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 24dd50938..2d8fa6070 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -692,6 +692,8 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target // Wait for an address (via Address Arbiter) static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout) { + NGLOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", + address, type, value, timeout); // If the passed address is a kernel virtual address, return invalid memory state. if ((address + 0x8000000000LL) < 0x7FFFE00000LL) { return ERR_INVALID_ADDRESS_STATE; @@ -715,6 +717,8 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout // Signals to an address (via Address Arbiter) static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to_wake) { + NGLOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", + address, type, value, 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; diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 0a76bd222..7a28f3c1c 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -232,7 +232,7 @@ public: Handle wait_handle; ///< The handle used to wait for the mutex. // If waiting for an AddressArbiter, this is the address being waited on. - VAddr arb_wait_address; + VAddr arb_wait_address{0}; ResultCode arb_wait_result{RESULT_SUCCESS}; ///< Result returned when done waiting on AddressArbiter. std::string name; -- cgit v1.2.3 From dc70a87af1576e29dd6fda1d0313aca260982498 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 21 Jun 2018 20:25:57 -0600 Subject: Kernel/Arbiters: HLE is atomic, adjust code to reflect that. --- src/core/hle/kernel/address_arbiter.cpp | 49 +++++++++------------------------ src/core/hle/kernel/thread.h | 1 - 2 files changed, 13 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 63cdcb559..01c5bf61b 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -20,14 +20,14 @@ namespace Kernel { ResultCode WaitForAddress(VAddr address, s64 timeout) { SharedPtr current_thread = GetCurrentThread(); current_thread->arb_wait_address = address; - current_thread->arb_wait_result = RESULT_TIMEOUT; current_thread->status = THREADSTATUS_WAIT_ARB; current_thread->wakeup_callback = nullptr; current_thread->WakeAfterDelay(timeout); Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); - return current_thread->arb_wait_result; + // This should never actually execute. + return RESULT_SUCCESS; } // Gets the threads waiting on an address. @@ -67,7 +67,7 @@ namespace Kernel { // TODO: Rescheduling should not occur while waking threads. How can it be prevented? for (size_t i = 0; i < last; i++) { ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB); - waiting_threads[i]->arb_wait_result = RESULT_SUCCESS; + waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); waiting_threads[i]->arb_wait_address = 0; waiting_threads[i]->ResumeFromWait(); } @@ -91,17 +91,9 @@ namespace Kernel { return ERR_INVALID_ADDRESS_STATE; } - s32 cur_value; - // Get value, incrementing if equal. - { - // Increment if Equal must be an atomic operation. - std::lock_guard lock(HLE::g_hle_lock); - cur_value = (s32)Memory::Read32(address); - if (cur_value == value) { - Memory::Write32(address, (u32)(cur_value + 1)); - } - } - if (cur_value != value) { + if ((s32)Memory::Read32(address) == value) { + Memory::Write32(address, (u32)(value + 1)); + } else { return ERR_INVALID_STATE; } @@ -128,18 +120,10 @@ namespace Kernel { } else { updated_value = value; } - s32 cur_value; - // Perform an atomic update if equal. - { - std::lock_guard lock(HLE::g_hle_lock); - cur_value = (s32)Memory::Read32(address); - if (cur_value == value) { - Memory::Write32(address, (u32)(updated_value)); - } - } - // Only continue if equal. - if (cur_value != value) { + if ((s32)Memory::Read32(address) == value) { + Memory::Write32(address, (u32)(updated_value)); + } else { return ERR_INVALID_STATE; } @@ -154,17 +138,10 @@ namespace Kernel { return ERR_INVALID_ADDRESS_STATE; } - s32 cur_value; - // Get value, decrementing if less than - { - // Decrement if less than must be an atomic operation. - std::lock_guard lock(HLE::g_hle_lock); - cur_value = (s32)Memory::Read32(address); - if (cur_value < value) { - Memory::Write32(address, (u32)(cur_value - 1)); - } - } - if (cur_value >= value) { + s32 cur_value = (s32)Memory::Read32(address); + if (cur_value < value) { + Memory::Write32(address, (u32)(cur_value - 1)); + } else { return ERR_INVALID_STATE; } // Short-circuit without rescheduling, if timeout is zero. diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 7a28f3c1c..3851d1085 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -233,7 +233,6 @@ public: // If waiting for an AddressArbiter, this is the address being waited on. VAddr arb_wait_address{0}; - ResultCode arb_wait_result{RESULT_SUCCESS}; ///< Result returned when done waiting on AddressArbiter. std::string name; -- cgit v1.2.3 From 08d454e30ddf5031190790c977bfda9422a24118 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 21 Jun 2018 21:05:34 -0600 Subject: Run clang-format on PR. --- src/core/hle/kernel/address_arbiter.cpp | 313 ++++++++++++++++---------------- src/core/hle/kernel/address_arbiter.h | 44 ++--- src/core/hle/kernel/thread.h | 4 +- 3 files changed, 181 insertions(+), 180 deletions(-) (limited to 'src') diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 01c5bf61b..972911e42 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -14,160 +14,161 @@ #include "core/memory.h" namespace Kernel { - namespace AddressArbiter { - - // Performs actual address waiting logic. - ResultCode WaitForAddress(VAddr address, s64 timeout) { - SharedPtr current_thread = GetCurrentThread(); - current_thread->arb_wait_address = address; - current_thread->status = THREADSTATUS_WAIT_ARB; - current_thread->wakeup_callback = nullptr; - - current_thread->WakeAfterDelay(timeout); - - Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); - // This should never actually execute. - return RESULT_SUCCESS; - } - - // Gets the threads waiting on an address. - void GetThreadsWaitingOnAddress(std::vector> &waiting_threads, VAddr address) { - auto RetrieveWaitingThreads = - [](size_t core_index, std::vector>& waiting_threads, VAddr arb_addr) { - const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); - auto& thread_list = scheduler->GetThreadList(); - - for (auto& thread : thread_list) { - if (thread->arb_wait_address == arb_addr) - waiting_threads.push_back(thread); - } - }; - - // Retrieve a list of all threads that are waiting for this address. - RetrieveWaitingThreads(0, waiting_threads, address); - RetrieveWaitingThreads(1, waiting_threads, address); - RetrieveWaitingThreads(2, waiting_threads, address); - RetrieveWaitingThreads(3, waiting_threads, address); - // Sort them by priority, such that the highest priority ones come first. - std::sort(waiting_threads.begin(), waiting_threads.end(), - [](const SharedPtr& lhs, const SharedPtr& rhs) { - return lhs->current_priority < rhs->current_priority; - }); - } - - // Wake up num_to_wake (or all) threads in a vector. - void WakeThreads(std::vector> &waiting_threads, s32 num_to_wake) { - // Only process up to 'target' threads, unless 'target' is <= 0, in which case process - // them all. - size_t last = waiting_threads.size(); - if (num_to_wake > 0) - last = num_to_wake; - - // Signal the waiting threads. - // TODO: Rescheduling should not occur while waking threads. How can it be prevented? - for (size_t i = 0; i < last; i++) { - ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB); - waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); - waiting_threads[i]->arb_wait_address = 0; - waiting_threads[i]->ResumeFromWait(); +namespace AddressArbiter { + +// Performs actual address waiting logic. +ResultCode WaitForAddress(VAddr address, s64 timeout) { + SharedPtr current_thread = GetCurrentThread(); + current_thread->arb_wait_address = address; + current_thread->status = THREADSTATUS_WAIT_ARB; + current_thread->wakeup_callback = nullptr; + + current_thread->WakeAfterDelay(timeout); + + Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); + // This should never actually execute. + return RESULT_SUCCESS; +} + +// Gets the threads waiting on an address. +void GetThreadsWaitingOnAddress(std::vector>& waiting_threads, VAddr address) { + auto RetrieveWaitingThreads = + [](size_t core_index, std::vector>& waiting_threads, VAddr arb_addr) { + const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); + auto& thread_list = scheduler->GetThreadList(); + + for (auto& thread : thread_list) { + if (thread->arb_wait_address == arb_addr) + waiting_threads.push_back(thread); } - - } - - // Signals an address being waited on. - ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { - // Get threads waiting on the address. - std::vector> waiting_threads; - GetThreadsWaitingOnAddress(waiting_threads, address); - - WakeThreads(waiting_threads, num_to_wake); - 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) { - // Ensure that we can write to the address. - if (!Memory::IsValidVirtualAddress(address)) { - return ERR_INVALID_ADDRESS_STATE; - } - - if ((s32)Memory::Read32(address) == value) { - Memory::Write32(address, (u32)(value + 1)); - } else { - return ERR_INVALID_STATE; - } - - return SignalToAddress(address, num_to_wake); - } - - // 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) { - // Ensure that we can write to the address. - if (!Memory::IsValidVirtualAddress(address)) { - return ERR_INVALID_ADDRESS_STATE; - } - - // Get threads waiting on the address. - std::vector> waiting_threads; - GetThreadsWaitingOnAddress(waiting_threads, address); - - // Determine the modified value depending on the waiting count. - s32 updated_value; - if (waiting_threads.size() == 0) { - updated_value = value - 1; - } else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) { - updated_value = value + 1; - } else { - updated_value = value; - } - - if ((s32)Memory::Read32(address) == value) { - Memory::Write32(address, (u32)(updated_value)); - } else { - return ERR_INVALID_STATE; - } - - WakeThreads(waiting_threads, num_to_wake); - 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) { - // Ensure that we can read the address. - if (!Memory::IsValidVirtualAddress(address)) { - return ERR_INVALID_ADDRESS_STATE; - } - - s32 cur_value = (s32)Memory::Read32(address); - if (cur_value < value) { - Memory::Write32(address, (u32)(cur_value - 1)); - } else { - return ERR_INVALID_STATE; - } - // Short-circuit without rescheduling, if timeout is zero. - if (timeout == 0) { - return RESULT_TIMEOUT; - } - - return WaitForAddress(address, timeout); - } - - // Waits on an address if the value passed is equal to the argument value. - ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { - // Ensure that we can read the address. - if (!Memory::IsValidVirtualAddress(address)) { - return ERR_INVALID_ADDRESS_STATE; - } - // Only wait for the address if equal. - if ((s32)Memory::Read32(address) != value) { - return ERR_INVALID_STATE; - } - // Short-circuit without rescheduling, if timeout is zero. - if (timeout == 0) { - return RESULT_TIMEOUT; - } - - return WaitForAddress(address, timeout); - } - } // namespace AddressArbiter -} // namespace Kernel \ No newline at end of file + }; + + // Retrieve a list of all threads that are waiting for this address. + RetrieveWaitingThreads(0, waiting_threads, address); + RetrieveWaitingThreads(1, waiting_threads, address); + RetrieveWaitingThreads(2, waiting_threads, address); + RetrieveWaitingThreads(3, waiting_threads, address); + // Sort them by priority, such that the highest priority ones come first. + std::sort(waiting_threads.begin(), waiting_threads.end(), + [](const SharedPtr& lhs, const SharedPtr& rhs) { + return lhs->current_priority < rhs->current_priority; + }); +} + +// Wake up num_to_wake (or all) threads in a vector. +void WakeThreads(std::vector>& waiting_threads, s32 num_to_wake) { + // Only process up to 'target' threads, unless 'target' is <= 0, in which case process + // them all. + size_t last = waiting_threads.size(); + if (num_to_wake > 0) + last = num_to_wake; + + // Signal the waiting threads. + // TODO: Rescheduling should not occur while waking threads. How can it be prevented? + for (size_t i = 0; i < last; i++) { + ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB); + waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); + waiting_threads[i]->arb_wait_address = 0; + waiting_threads[i]->ResumeFromWait(); + } +} + +// Signals an address being waited on. +ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { + // Get threads waiting on the address. + std::vector> waiting_threads; + GetThreadsWaitingOnAddress(waiting_threads, address); + + WakeThreads(waiting_threads, num_to_wake); + 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) { + // Ensure that we can write to the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + + if ((s32)Memory::Read32(address) == value) { + Memory::Write32(address, (u32)(value + 1)); + } else { + return ERR_INVALID_STATE; + } + + return SignalToAddress(address, num_to_wake); +} + +// 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) { + // Ensure that we can write to the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + + // Get threads waiting on the address. + std::vector> waiting_threads; + GetThreadsWaitingOnAddress(waiting_threads, address); + + // Determine the modified value depending on the waiting count. + s32 updated_value; + if (waiting_threads.size() == 0) { + updated_value = value - 1; + } else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) { + updated_value = value + 1; + } else { + updated_value = value; + } + + if ((s32)Memory::Read32(address) == value) { + Memory::Write32(address, (u32)(updated_value)); + } else { + return ERR_INVALID_STATE; + } + + WakeThreads(waiting_threads, num_to_wake); + 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) { + // Ensure that we can read the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + + s32 cur_value = (s32)Memory::Read32(address); + if (cur_value < value) { + Memory::Write32(address, (u32)(cur_value - 1)); + } else { + return ERR_INVALID_STATE; + } + // Short-circuit without rescheduling, if timeout is zero. + if (timeout == 0) { + return RESULT_TIMEOUT; + } + + return WaitForAddress(address, timeout); +} + +// Waits on an address if the value passed is equal to the argument value. +ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { + // Ensure that we can read the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + // Only wait for the address if equal. + if ((s32)Memory::Read32(address) != value) { + return ERR_INVALID_STATE; + } + // Short-circuit without rescheduling, if timeout is zero. + if (timeout == 0) { + return RESULT_TIMEOUT; + } + + return WaitForAddress(address, timeout); +} +} // namespace AddressArbiter +} // namespace Kernel diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 32d4a77a9..f20f3dbc0 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -8,25 +8,25 @@ 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 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 +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 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 diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 3851d1085..f1e759802 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -228,8 +228,8 @@ public: // If waiting on a ConditionVariable, this is the ConditionVariable address VAddr condvar_wait_address; - VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address - Handle wait_handle; ///< The handle used to wait for the mutex. + VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address + Handle wait_handle; ///< The handle used to wait for the mutex. // If waiting for an AddressArbiter, this is the address being waited on. VAddr arb_wait_address{0}; -- cgit v1.2.3 From 5f8aa02584eb8a5fdba583a192d551a2db707fb7 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 21 Jun 2018 21:09:51 -0600 Subject: Add additional missing format. --- src/core/hle/kernel/svc.cpp | 40 +++++++++++++++++++++------------------- src/core/hle/kernel/svc_wrap.h | 8 ++++++-- 2 files changed, 27 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 2d8fa6070..95ce2205a 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -693,7 +693,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target // Wait for an address (via Address Arbiter) static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout) { NGLOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", - address, type, value, timeout); + address, type, value, timeout); // If the passed address is a kernel virtual address, return invalid memory state. if ((address + 0x8000000000LL) < 0x7FFFE00000LL) { return ERR_INVALID_ADDRESS_STATE; @@ -704,21 +704,22 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout } 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; + 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) { - NGLOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", - address, type, value, num_to_wake); + NGLOG_WARNING(Kernel_SVC, + "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", address, + type, value, 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; @@ -729,14 +730,15 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to } switch ((AddressArbiter::SignalType)type) { - case AddressArbiter::SignalType::Signal: - return AddressArbiter::SignalToAddress(address, 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; + case AddressArbiter::SignalType::Signal: + return AddressArbiter::SignalToAddress(address, 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; } } diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index efae4fdfe..79c3fe31b 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -181,12 +181,16 @@ void SvcWrap() { template void SvcWrap() { - FuncReturn(func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3)).raw); + FuncReturn( + func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3)) + .raw); } template void SvcWrap() { - FuncReturn(func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), (s32)(PARAM(3) & 0xFFFFFFFF)).raw); + FuncReturn(func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), + (s32)(PARAM(3) & 0xFFFFFFFF)) + .raw); } //////////////////////////////////////////////////////////////////////////////////////////////////// -- cgit v1.2.3 From 067ac434ba90084359babef1638970e849a5f2ce Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 22 Jun 2018 00:47:59 -0600 Subject: Kernel/Arbiters: Fix casts, cleanup comments/magic numbers --- src/core/hle/kernel/address_arbiter.cpp | 25 ++++++++++++------------- src/core/hle/kernel/svc.cpp | 8 ++++---- src/core/memory.cpp | 4 ++++ src/core/memory.h | 7 +++++++ 4 files changed, 27 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 972911e42..e9c8369d7 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -17,7 +17,7 @@ namespace Kernel { namespace AddressArbiter { // Performs actual address waiting logic. -ResultCode WaitForAddress(VAddr address, s64 timeout) { +static ResultCode WaitForAddress(VAddr address, s64 timeout) { SharedPtr current_thread = GetCurrentThread(); current_thread->arb_wait_address = address; current_thread->status = THREADSTATUS_WAIT_ARB; @@ -26,12 +26,12 @@ ResultCode WaitForAddress(VAddr address, s64 timeout) { current_thread->WakeAfterDelay(timeout); Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); - // This should never actually execute. - return RESULT_SUCCESS; + return RESULT_TIMEOUT; } // Gets the threads waiting on an address. -void GetThreadsWaitingOnAddress(std::vector>& waiting_threads, VAddr address) { +static void GetThreadsWaitingOnAddress(std::vector>& waiting_threads, + VAddr address) { auto RetrieveWaitingThreads = [](size_t core_index, std::vector>& waiting_threads, VAddr arb_addr) { const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); @@ -56,7 +56,7 @@ void GetThreadsWaitingOnAddress(std::vector>& waiting_threads, } // Wake up num_to_wake (or all) threads in a vector. -void WakeThreads(std::vector>& waiting_threads, s32 num_to_wake) { +static void WakeThreads(std::vector>& waiting_threads, s32 num_to_wake) { // Only process up to 'target' threads, unless 'target' is <= 0, in which case process // them all. size_t last = waiting_threads.size(); @@ -64,7 +64,6 @@ void WakeThreads(std::vector>& waiting_threads, s32 num_to_wak last = num_to_wake; // Signal the waiting threads. - // TODO: Rescheduling should not occur while waking threads. How can it be prevented? for (size_t i = 0; i < last; i++) { ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB); waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); @@ -90,8 +89,8 @@ ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_ return ERR_INVALID_ADDRESS_STATE; } - if ((s32)Memory::Read32(address) == value) { - Memory::Write32(address, (u32)(value + 1)); + if (static_cast(Memory::Read32(address)) == value) { + Memory::Write32(address, static_cast(value + 1)); } else { return ERR_INVALID_STATE; } @@ -122,8 +121,8 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu updated_value = value; } - if ((s32)Memory::Read32(address) == value) { - Memory::Write32(address, (u32)(updated_value)); + if (static_cast(Memory::Read32(address)) == value) { + Memory::Write32(address, static_cast(updated_value)); } else { return ERR_INVALID_STATE; } @@ -139,9 +138,9 @@ ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool return ERR_INVALID_ADDRESS_STATE; } - s32 cur_value = (s32)Memory::Read32(address); + s32 cur_value = static_cast(Memory::Read32(address)); if (cur_value < value) { - Memory::Write32(address, (u32)(cur_value - 1)); + Memory::Write32(address, static_cast(cur_value - 1)); } else { return ERR_INVALID_STATE; } @@ -160,7 +159,7 @@ ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { return ERR_INVALID_ADDRESS_STATE; } // Only wait for the address if equal. - if ((s32)Memory::Read32(address) != value) { + if (static_cast(Memory::Read32(address)) != value) { return ERR_INVALID_STATE; } // Short-circuit without rescheduling, if timeout is zero. diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 95ce2205a..1a36e0d02 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -695,7 +695,7 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout NGLOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address, type, value, timeout); // If the passed address is a kernel virtual address, return invalid memory state. - if ((address + 0x8000000000LL) < 0x7FFFE00000LL) { + if (Memory::IsKernelVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } // If the address is not properly aligned to 4 bytes, return invalid address. @@ -703,7 +703,7 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout return ERR_INVALID_ADDRESS; } - switch ((AddressArbiter::ArbitrationType)type) { + switch (static_cast(type)) { case AddressArbiter::ArbitrationType::WaitIfLessThan: return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, false); case AddressArbiter::ArbitrationType::DecrementAndWaitIfLessThan: @@ -721,7 +721,7 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", address, type, value, num_to_wake); // If the passed address is a kernel virtual address, return invalid memory state. - if ((address + 0x8000000000LL) < 0x7FFFE00000LL) { + if (Memory::IsKernelVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } // If the address is not properly aligned to 4 bytes, return invalid address. @@ -729,7 +729,7 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to return ERR_INVALID_ADDRESS; } - switch ((AddressArbiter::SignalType)type) { + switch (static_cast(type)) { case AddressArbiter::SignalType::Signal: return AddressArbiter::SignalToAddress(address, num_to_wake); case AddressArbiter::SignalType::IncrementAndSignalIfEqual: diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 3b81acd63..f070dee7d 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -241,6 +241,10 @@ bool IsValidVirtualAddress(const VAddr vaddr) { return IsValidVirtualAddress(*Core::CurrentProcess(), vaddr); } +bool IsKernelVirtualAddress(const VAddr vaddr) { + return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; +} + bool IsValidPhysicalAddress(const PAddr paddr) { return GetPhysicalPointer(paddr) != nullptr; } diff --git a/src/core/memory.h b/src/core/memory.h index 3f56a2c6a..8d5d017a4 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -188,6 +188,11 @@ enum : VAddr { MAP_REGION_VADDR = NEW_MAP_REGION_VADDR_END, MAP_REGION_SIZE = 0x1000000000, MAP_REGION_VADDR_END = MAP_REGION_VADDR + MAP_REGION_SIZE, + + /// Kernel Virtual Address Range + KERNEL_REGION_VADDR = 0xFFFFFF8000000000, + KERNEL_REGION_SIZE = 0x7FFFE00000, + KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE, }; /// Currently active page table @@ -197,6 +202,8 @@ PageTable* GetCurrentPageTable(); /// Determines if the given VAddr is valid for the specified process. bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr); bool IsValidVirtualAddress(const VAddr addr); +/// Determines if the given VAddr is a kernel address +bool IsKernelVirtualAddress(const VAddr addr); bool IsValidPhysicalAddress(const PAddr addr); -- cgit v1.2.3