SvcThreadSync.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. using ChocolArm64.State;
  2. using Ryujinx.HLE.Logging;
  3. using Ryujinx.HLE.OsHle.Handles;
  4. using System;
  5. using static Ryujinx.HLE.OsHle.ErrorCode;
  6. namespace Ryujinx.HLE.OsHle.Kernel
  7. {
  8. partial class SvcHandler
  9. {
  10. private const int MutexHasListenersMask = 0x40000000;
  11. private void SvcArbitrateLock(AThreadState ThreadState)
  12. {
  13. int OwnerThreadHandle = (int)ThreadState.X0;
  14. long MutexAddress = (long)ThreadState.X1;
  15. int WaitThreadHandle = (int)ThreadState.X2;
  16. Ns.Log.PrintDebug(LogClass.KernelSvc,
  17. "OwnerThreadHandle = " + OwnerThreadHandle.ToString("x8") + ", " +
  18. "MutexAddress = " + MutexAddress .ToString("x16") + ", " +
  19. "WaitThreadHandle = " + WaitThreadHandle .ToString("x8"));
  20. if (IsPointingInsideKernel(MutexAddress))
  21. {
  22. Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
  23. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
  24. return;
  25. }
  26. if (IsWordAddressUnaligned(MutexAddress))
  27. {
  28. Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
  29. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
  30. return;
  31. }
  32. KThread OwnerThread = Process.HandleTable.GetData<KThread>(OwnerThreadHandle);
  33. if (OwnerThread == null)
  34. {
  35. Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid owner thread handle 0x{OwnerThreadHandle:x8}!");
  36. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
  37. return;
  38. }
  39. KThread WaitThread = Process.HandleTable.GetData<KThread>(WaitThreadHandle);
  40. if (WaitThread == null)
  41. {
  42. Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid requesting thread handle 0x{WaitThreadHandle:x8}!");
  43. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
  44. return;
  45. }
  46. KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
  47. MutexLock(CurrThread, WaitThread, OwnerThreadHandle, WaitThreadHandle, MutexAddress);
  48. ThreadState.X0 = 0;
  49. }
  50. private void SvcArbitrateUnlock(AThreadState ThreadState)
  51. {
  52. long MutexAddress = (long)ThreadState.X0;
  53. Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexAddress = " + MutexAddress.ToString("x16"));
  54. if (IsPointingInsideKernel(MutexAddress))
  55. {
  56. Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
  57. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
  58. return;
  59. }
  60. if (IsWordAddressUnaligned(MutexAddress))
  61. {
  62. Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
  63. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
  64. return;
  65. }
  66. MutexUnlock(Process.GetThread(ThreadState.Tpidr), MutexAddress);
  67. ThreadState.X0 = 0;
  68. }
  69. private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState)
  70. {
  71. long MutexAddress = (long)ThreadState.X0;
  72. long CondVarAddress = (long)ThreadState.X1;
  73. int ThreadHandle = (int)ThreadState.X2;
  74. ulong Timeout = ThreadState.X3;
  75. Ns.Log.PrintDebug(LogClass.KernelSvc,
  76. "MutexAddress = " + MutexAddress .ToString("x16") + ", " +
  77. "CondVarAddress = " + CondVarAddress.ToString("x16") + ", " +
  78. "ThreadHandle = " + ThreadHandle .ToString("x8") + ", " +
  79. "Timeout = " + Timeout .ToString("x16"));
  80. if (IsPointingInsideKernel(MutexAddress))
  81. {
  82. Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
  83. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
  84. return;
  85. }
  86. if (IsWordAddressUnaligned(MutexAddress))
  87. {
  88. Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
  89. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
  90. return;
  91. }
  92. KThread Thread = Process.HandleTable.GetData<KThread>(ThreadHandle);
  93. if (Thread == null)
  94. {
  95. Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!");
  96. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
  97. return;
  98. }
  99. KThread WaitThread = Process.GetThread(ThreadState.Tpidr);
  100. if (!CondVarWait(WaitThread, ThreadHandle, MutexAddress, CondVarAddress, Timeout))
  101. {
  102. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
  103. return;
  104. }
  105. ThreadState.X0 = 0;
  106. }
  107. private void SvcSignalProcessWideKey(AThreadState ThreadState)
  108. {
  109. long CondVarAddress = (long)ThreadState.X0;
  110. int Count = (int)ThreadState.X1;
  111. Ns.Log.PrintDebug(LogClass.KernelSvc,
  112. "CondVarAddress = " + CondVarAddress.ToString("x16") + ", " +
  113. "Count = " + Count .ToString("x8"));
  114. KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
  115. CondVarSignal(ThreadState, CurrThread, CondVarAddress, Count);
  116. ThreadState.X0 = 0;
  117. }
  118. private void MutexLock(
  119. KThread CurrThread,
  120. KThread WaitThread,
  121. int OwnerThreadHandle,
  122. int WaitThreadHandle,
  123. long MutexAddress)
  124. {
  125. lock (Process.ThreadSyncLock)
  126. {
  127. int MutexValue = Memory.ReadInt32(MutexAddress);
  128. Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = " + MutexValue.ToString("x8"));
  129. if (MutexValue != (OwnerThreadHandle | MutexHasListenersMask))
  130. {
  131. return;
  132. }
  133. CurrThread.WaitHandle = WaitThreadHandle;
  134. CurrThread.MutexAddress = MutexAddress;
  135. InsertWaitingMutexThreadUnsafe(OwnerThreadHandle, WaitThread);
  136. }
  137. Ns.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state...");
  138. Process.Scheduler.EnterWait(CurrThread);
  139. }
  140. private void MutexUnlock(KThread CurrThread, long MutexAddress)
  141. {
  142. lock (Process.ThreadSyncLock)
  143. {
  144. //This is the new thread that will now own the mutex.
  145. //If no threads are waiting for the lock, then it should be null.
  146. (KThread OwnerThread, int Count) = PopMutexThreadUnsafe(CurrThread, MutexAddress);
  147. if (OwnerThread == CurrThread)
  148. {
  149. throw new InvalidOperationException();
  150. }
  151. if (OwnerThread != null)
  152. {
  153. //Remove all waiting mutex from the old owner,
  154. //and insert then on the new owner.
  155. UpdateMutexOwnerUnsafe(CurrThread, OwnerThread, MutexAddress);
  156. CurrThread.UpdatePriority();
  157. int HasListeners = Count >= 2 ? MutexHasListenersMask : 0;
  158. Memory.WriteInt32ToSharedAddr(MutexAddress, HasListeners | OwnerThread.WaitHandle);
  159. OwnerThread.WaitHandle = 0;
  160. OwnerThread.MutexAddress = 0;
  161. OwnerThread.CondVarAddress = 0;
  162. OwnerThread.MutexOwner = null;
  163. OwnerThread.UpdatePriority();
  164. Process.Scheduler.WakeUp(OwnerThread);
  165. Ns.Log.PrintDebug(LogClass.KernelSvc, "Gave mutex to thread id " + OwnerThread.ThreadId + "!");
  166. }
  167. else
  168. {
  169. Memory.WriteInt32ToSharedAddr(MutexAddress, 0);
  170. Ns.Log.PrintDebug(LogClass.KernelSvc, "No threads waiting mutex!");
  171. }
  172. }
  173. }
  174. private bool CondVarWait(
  175. KThread WaitThread,
  176. int WaitThreadHandle,
  177. long MutexAddress,
  178. long CondVarAddress,
  179. ulong Timeout)
  180. {
  181. WaitThread.WaitHandle = WaitThreadHandle;
  182. WaitThread.MutexAddress = MutexAddress;
  183. WaitThread.CondVarAddress = CondVarAddress;
  184. lock (Process.ThreadSyncLock)
  185. {
  186. MutexUnlock(WaitThread, MutexAddress);
  187. WaitThread.CondVarSignaled = false;
  188. Process.ThreadArbiterList.Add(WaitThread);
  189. }
  190. Ns.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state...");
  191. if (Timeout != ulong.MaxValue)
  192. {
  193. Process.Scheduler.EnterWait(WaitThread, NsTimeConverter.GetTimeMs(Timeout));
  194. lock (Process.ThreadSyncLock)
  195. {
  196. if (!WaitThread.CondVarSignaled || WaitThread.MutexOwner != null)
  197. {
  198. if (WaitThread.MutexOwner != null)
  199. {
  200. WaitThread.MutexOwner.MutexWaiters.Remove(WaitThread);
  201. WaitThread.MutexOwner.UpdatePriority();
  202. WaitThread.MutexOwner = null;
  203. }
  204. Process.ThreadArbiterList.Remove(WaitThread);
  205. Ns.Log.PrintDebug(LogClass.KernelSvc, "Timed out...");
  206. return false;
  207. }
  208. }
  209. }
  210. else
  211. {
  212. Process.Scheduler.EnterWait(WaitThread);
  213. }
  214. return true;
  215. }
  216. private void CondVarSignal(
  217. AThreadState ThreadState,
  218. KThread CurrThread,
  219. long CondVarAddress,
  220. int Count)
  221. {
  222. lock (Process.ThreadSyncLock)
  223. {
  224. while (Count == -1 || Count-- > 0)
  225. {
  226. KThread WaitThread = PopCondVarThreadUnsafe(CondVarAddress);
  227. if (WaitThread == null)
  228. {
  229. Ns.Log.PrintDebug(LogClass.KernelSvc, "No more threads to wake up!");
  230. break;
  231. }
  232. WaitThread.CondVarSignaled = true;
  233. long MutexAddress = WaitThread.MutexAddress;
  234. Memory.SetExclusive(ThreadState, MutexAddress);
  235. int MutexValue = Memory.ReadInt32(MutexAddress);
  236. while (MutexValue != 0)
  237. {
  238. if (Memory.TestExclusive(ThreadState, MutexAddress))
  239. {
  240. //Wait until the lock is released.
  241. InsertWaitingMutexThreadUnsafe(MutexValue & ~MutexHasListenersMask, WaitThread);
  242. Memory.WriteInt32(MutexAddress, MutexValue | MutexHasListenersMask);
  243. Memory.ClearExclusiveForStore(ThreadState);
  244. break;
  245. }
  246. Memory.SetExclusive(ThreadState, MutexAddress);
  247. MutexValue = Memory.ReadInt32(MutexAddress);
  248. }
  249. Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = " + MutexValue.ToString("x8"));
  250. if (MutexValue == 0)
  251. {
  252. //Give the lock to this thread.
  253. Memory.WriteInt32ToSharedAddr(MutexAddress, WaitThread.WaitHandle);
  254. WaitThread.WaitHandle = 0;
  255. WaitThread.MutexAddress = 0;
  256. WaitThread.CondVarAddress = 0;
  257. WaitThread.MutexOwner?.UpdatePriority();
  258. WaitThread.MutexOwner = null;
  259. Process.Scheduler.WakeUp(WaitThread);
  260. }
  261. }
  262. }
  263. }
  264. private void UpdateMutexOwnerUnsafe(KThread CurrThread, KThread NewOwner, long MutexAddress)
  265. {
  266. //Go through all threads waiting for the mutex,
  267. //and update the MutexOwner field to point to the new owner.
  268. for (int Index = 0; Index < CurrThread.MutexWaiters.Count; Index++)
  269. {
  270. KThread Thread = CurrThread.MutexWaiters[Index];
  271. if (Thread.MutexAddress == MutexAddress)
  272. {
  273. CurrThread.MutexWaiters.RemoveAt(Index--);
  274. InsertWaitingMutexThreadUnsafe(NewOwner, Thread);
  275. }
  276. }
  277. }
  278. private void InsertWaitingMutexThreadUnsafe(int OwnerThreadHandle, KThread WaitThread)
  279. {
  280. KThread OwnerThread = Process.HandleTable.GetData<KThread>(OwnerThreadHandle);
  281. if (OwnerThread == null)
  282. {
  283. Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{OwnerThreadHandle:x8}!");
  284. return;
  285. }
  286. InsertWaitingMutexThreadUnsafe(OwnerThread, WaitThread);
  287. }
  288. private void InsertWaitingMutexThreadUnsafe(KThread OwnerThread, KThread WaitThread)
  289. {
  290. WaitThread.MutexOwner = OwnerThread;
  291. if (!OwnerThread.MutexWaiters.Contains(WaitThread))
  292. {
  293. OwnerThread.MutexWaiters.Add(WaitThread);
  294. OwnerThread.UpdatePriority();
  295. }
  296. }
  297. private (KThread, int) PopMutexThreadUnsafe(KThread OwnerThread, long MutexAddress)
  298. {
  299. int Count = 0;
  300. KThread WakeThread = null;
  301. foreach (KThread Thread in OwnerThread.MutexWaiters)
  302. {
  303. if (Thread.MutexAddress != MutexAddress)
  304. {
  305. continue;
  306. }
  307. if (WakeThread == null || Thread.ActualPriority < WakeThread.ActualPriority)
  308. {
  309. WakeThread = Thread;
  310. }
  311. Count++;
  312. }
  313. if (WakeThread != null)
  314. {
  315. OwnerThread.MutexWaiters.Remove(WakeThread);
  316. }
  317. return (WakeThread, Count);
  318. }
  319. private KThread PopCondVarThreadUnsafe(long CondVarAddress)
  320. {
  321. KThread WakeThread = null;
  322. foreach (KThread Thread in Process.ThreadArbiterList)
  323. {
  324. if (Thread.CondVarAddress != CondVarAddress)
  325. {
  326. continue;
  327. }
  328. if (WakeThread == null || Thread.ActualPriority < WakeThread.ActualPriority)
  329. {
  330. WakeThread = Thread;
  331. }
  332. }
  333. if (WakeThread != null)
  334. {
  335. Process.ThreadArbiterList.Remove(WakeThread);
  336. }
  337. return WakeThread;
  338. }
  339. private bool IsPointingInsideKernel(long Address)
  340. {
  341. return ((ulong)Address + 0x1000000000) < 0xffffff000;
  342. }
  343. private bool IsWordAddressUnaligned(long Address)
  344. {
  345. return (Address & 3) != 0;
  346. }
  347. }
  348. }