mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-10-30 23:49:01 -05:00 
			
		
		
		
	Merge pull request #579 from SciresM/master
svc: Fully implement svcSignalToAddress and svcWaitForAddress
This commit is contained in:
		
							
								
								
									
										173
									
								
								src/core/hle/kernel/address_arbiter.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								src/core/hle/kernel/address_arbiter.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,173 @@ | ||||
| // 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/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. | ||||
| static ResultCode WaitForAddress(VAddr address, s64 timeout) { | ||||
|     SharedPtr<Thread> 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(); | ||||
|     return RESULT_TIMEOUT; | ||||
| } | ||||
|  | ||||
| // Gets the threads waiting on an address. | ||||
| static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads, | ||||
|                                        VAddr address) { | ||||
|     auto RetrieveWaitingThreads = | ||||
|         [](size_t core_index, std::vector<SharedPtr<Thread>>& 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<Thread>& lhs, const SharedPtr<Thread>& rhs) { | ||||
|                   return lhs->current_priority < rhs->current_priority; | ||||
|               }); | ||||
| } | ||||
|  | ||||
| // Wake up num_to_wake (or all) threads in a vector. | ||||
| static void WakeThreads(std::vector<SharedPtr<Thread>>& 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. | ||||
|     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<SharedPtr<Thread>> 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 (static_cast<s32>(Memory::Read32(address)) == value) { | ||||
|         Memory::Write32(address, static_cast<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<SharedPtr<Thread>> 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 (static_cast<s32>(Memory::Read32(address)) == value) { | ||||
|         Memory::Write32(address, static_cast<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 = static_cast<s32>(Memory::Read32(address)); | ||||
|     if (cur_value < value) { | ||||
|         Memory::Write32(address, static_cast<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 (static_cast<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 | ||||
							
								
								
									
										32
									
								
								src/core/hle/kernel/address_arbiter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/core/hle/kernel/address_arbiter.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 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 | ||||
| @@ -20,13 +20,16 @@ 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, | ||||
|     InvalidState = 125, | ||||
| }; | ||||
| } | ||||
|  | ||||
| @@ -39,14 +42,15 @@ 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_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); | ||||
| constexpr ResultCode ERR_INVALID_POINTER(-1); | ||||
| constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1); | ||||
| constexpr ResultCode ERR_NOT_AUTHORIZED(-1); | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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,58 @@ 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) { | ||||
|     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 (Memory::IsKernelVirtualAddress(address)) { | ||||
|         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 (static_cast<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) { | ||||
|     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 (Memory::IsKernelVirtualAddress(address)) { | ||||
|         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 (static_cast<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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// This returns the total CPU ticks elapsed since the CPU was powered-on | ||||
| static u64 GetSystemTick() { | ||||
|     const u64 result{CoreTiming::GetTicks()}; | ||||
| @@ -861,8 +914,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"}, | ||||
|   | ||||
| @@ -179,6 +179,20 @@ 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 | ||||
|  | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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 | ||||
| }; | ||||
| @@ -230,6 +231,9 @@ public: | ||||
|     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}; | ||||
|  | ||||
|     std::string name; | ||||
|  | ||||
|     /// Handle used by guest emulated application to access this thread | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 bunnei
					bunnei