SvcThreadSync.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  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 SvcWaitForAddress(AThreadState ThreadState)
  141. {
  142. long Address = (long)ThreadState.X0;
  143. ArbitrationType Type = (ArbitrationType)ThreadState.X1;
  144. int Value = (int)ThreadState.X2;
  145. ulong Timeout = ThreadState.X3;
  146. Ns.Log.PrintDebug(LogClass.KernelSvc,
  147. "Address = " + Address.ToString("x16") + ", " +
  148. "ArbitrationType = " + Type .ToString() + ", " +
  149. "Value = " + Value .ToString("x8") + ", " +
  150. "Timeout = " + Timeout.ToString("x16"));
  151. if (IsPointingInsideKernel(Address))
  152. {
  153. Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!");
  154. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
  155. return;
  156. }
  157. if (IsWordAddressUnaligned(Address))
  158. {
  159. Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!");
  160. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
  161. return;
  162. }
  163. switch (Type)
  164. {
  165. case ArbitrationType.WaitIfLessThan:
  166. ThreadState.X0 = AddressArbiter.WaitForAddressIfLessThan(Process, ThreadState, Memory, Address, Value, Timeout, false);
  167. break;
  168. case ArbitrationType.DecrementAndWaitIfLessThan:
  169. ThreadState.X0 = AddressArbiter.WaitForAddressIfLessThan(Process, ThreadState, Memory, Address, Value, Timeout, true);
  170. break;
  171. case ArbitrationType.WaitIfEqual:
  172. ThreadState.X0 = AddressArbiter.WaitForAddressIfEqual(Process, ThreadState, Memory, Address, Value, Timeout);
  173. break;
  174. default:
  175. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
  176. break;
  177. }
  178. }
  179. private void MutexUnlock(KThread CurrThread, long MutexAddress)
  180. {
  181. lock (Process.ThreadSyncLock)
  182. {
  183. //This is the new thread that will now own the mutex.
  184. //If no threads are waiting for the lock, then it should be null.
  185. (KThread OwnerThread, int Count) = PopMutexThreadUnsafe(CurrThread, MutexAddress);
  186. if (OwnerThread == CurrThread)
  187. {
  188. throw new InvalidOperationException();
  189. }
  190. if (OwnerThread != null)
  191. {
  192. //Remove all waiting mutex from the old owner,
  193. //and insert then on the new owner.
  194. UpdateMutexOwnerUnsafe(CurrThread, OwnerThread, MutexAddress);
  195. CurrThread.UpdatePriority();
  196. int HasListeners = Count >= 2 ? MutexHasListenersMask : 0;
  197. Memory.WriteInt32ToSharedAddr(MutexAddress, HasListeners | OwnerThread.WaitHandle);
  198. OwnerThread.WaitHandle = 0;
  199. OwnerThread.MutexAddress = 0;
  200. OwnerThread.CondVarAddress = 0;
  201. OwnerThread.MutexOwner = null;
  202. OwnerThread.UpdatePriority();
  203. Process.Scheduler.WakeUp(OwnerThread);
  204. Ns.Log.PrintDebug(LogClass.KernelSvc, "Gave mutex to thread id " + OwnerThread.ThreadId + "!");
  205. }
  206. else
  207. {
  208. Memory.WriteInt32ToSharedAddr(MutexAddress, 0);
  209. Ns.Log.PrintDebug(LogClass.KernelSvc, "No threads waiting mutex!");
  210. }
  211. }
  212. }
  213. private bool CondVarWait(
  214. KThread WaitThread,
  215. int WaitThreadHandle,
  216. long MutexAddress,
  217. long CondVarAddress,
  218. ulong Timeout)
  219. {
  220. WaitThread.WaitHandle = WaitThreadHandle;
  221. WaitThread.MutexAddress = MutexAddress;
  222. WaitThread.CondVarAddress = CondVarAddress;
  223. lock (Process.ThreadSyncLock)
  224. {
  225. MutexUnlock(WaitThread, MutexAddress);
  226. WaitThread.CondVarSignaled = false;
  227. Process.ThreadArbiterList.Add(WaitThread);
  228. }
  229. Ns.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state...");
  230. if (Timeout != ulong.MaxValue)
  231. {
  232. Process.Scheduler.EnterWait(WaitThread, NsTimeConverter.GetTimeMs(Timeout));
  233. lock (Process.ThreadSyncLock)
  234. {
  235. if (!WaitThread.CondVarSignaled || WaitThread.MutexOwner != null)
  236. {
  237. if (WaitThread.MutexOwner != null)
  238. {
  239. WaitThread.MutexOwner.MutexWaiters.Remove(WaitThread);
  240. WaitThread.MutexOwner.UpdatePriority();
  241. WaitThread.MutexOwner = null;
  242. }
  243. Process.ThreadArbiterList.Remove(WaitThread);
  244. Ns.Log.PrintDebug(LogClass.KernelSvc, "Timed out...");
  245. return false;
  246. }
  247. }
  248. }
  249. else
  250. {
  251. Process.Scheduler.EnterWait(WaitThread);
  252. }
  253. return true;
  254. }
  255. private void CondVarSignal(
  256. AThreadState ThreadState,
  257. KThread CurrThread,
  258. long CondVarAddress,
  259. int Count)
  260. {
  261. lock (Process.ThreadSyncLock)
  262. {
  263. while (Count == -1 || Count-- > 0)
  264. {
  265. KThread WaitThread = PopCondVarThreadUnsafe(CondVarAddress);
  266. if (WaitThread == null)
  267. {
  268. Ns.Log.PrintDebug(LogClass.KernelSvc, "No more threads to wake up!");
  269. break;
  270. }
  271. WaitThread.CondVarSignaled = true;
  272. long MutexAddress = WaitThread.MutexAddress;
  273. Memory.SetExclusive(ThreadState, MutexAddress);
  274. int MutexValue = Memory.ReadInt32(MutexAddress);
  275. while (MutexValue != 0)
  276. {
  277. if (Memory.TestExclusive(ThreadState, MutexAddress))
  278. {
  279. //Wait until the lock is released.
  280. InsertWaitingMutexThreadUnsafe(MutexValue & ~MutexHasListenersMask, WaitThread);
  281. Memory.WriteInt32(MutexAddress, MutexValue | MutexHasListenersMask);
  282. Memory.ClearExclusiveForStore(ThreadState);
  283. break;
  284. }
  285. Memory.SetExclusive(ThreadState, MutexAddress);
  286. MutexValue = Memory.ReadInt32(MutexAddress);
  287. }
  288. Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = " + MutexValue.ToString("x8"));
  289. if (MutexValue == 0)
  290. {
  291. //Give the lock to this thread.
  292. Memory.WriteInt32ToSharedAddr(MutexAddress, WaitThread.WaitHandle);
  293. WaitThread.WaitHandle = 0;
  294. WaitThread.MutexAddress = 0;
  295. WaitThread.CondVarAddress = 0;
  296. WaitThread.MutexOwner?.UpdatePriority();
  297. WaitThread.MutexOwner = null;
  298. Process.Scheduler.WakeUp(WaitThread);
  299. }
  300. }
  301. }
  302. }
  303. private void UpdateMutexOwnerUnsafe(KThread CurrThread, KThread NewOwner, long MutexAddress)
  304. {
  305. //Go through all threads waiting for the mutex,
  306. //and update the MutexOwner field to point to the new owner.
  307. for (int Index = 0; Index < CurrThread.MutexWaiters.Count; Index++)
  308. {
  309. KThread Thread = CurrThread.MutexWaiters[Index];
  310. if (Thread.MutexAddress == MutexAddress)
  311. {
  312. CurrThread.MutexWaiters.RemoveAt(Index--);
  313. InsertWaitingMutexThreadUnsafe(NewOwner, Thread);
  314. }
  315. }
  316. }
  317. private void InsertWaitingMutexThreadUnsafe(int OwnerThreadHandle, KThread WaitThread)
  318. {
  319. KThread OwnerThread = Process.HandleTable.GetData<KThread>(OwnerThreadHandle);
  320. if (OwnerThread == null)
  321. {
  322. Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{OwnerThreadHandle:x8}!");
  323. return;
  324. }
  325. InsertWaitingMutexThreadUnsafe(OwnerThread, WaitThread);
  326. }
  327. private void InsertWaitingMutexThreadUnsafe(KThread OwnerThread, KThread WaitThread)
  328. {
  329. WaitThread.MutexOwner = OwnerThread;
  330. if (!OwnerThread.MutexWaiters.Contains(WaitThread))
  331. {
  332. OwnerThread.MutexWaiters.Add(WaitThread);
  333. OwnerThread.UpdatePriority();
  334. }
  335. }
  336. private (KThread, int) PopMutexThreadUnsafe(KThread OwnerThread, long MutexAddress)
  337. {
  338. int Count = 0;
  339. KThread WakeThread = null;
  340. foreach (KThread Thread in OwnerThread.MutexWaiters)
  341. {
  342. if (Thread.MutexAddress != MutexAddress)
  343. {
  344. continue;
  345. }
  346. if (WakeThread == null || Thread.ActualPriority < WakeThread.ActualPriority)
  347. {
  348. WakeThread = Thread;
  349. }
  350. Count++;
  351. }
  352. if (WakeThread != null)
  353. {
  354. OwnerThread.MutexWaiters.Remove(WakeThread);
  355. }
  356. return (WakeThread, Count);
  357. }
  358. private KThread PopCondVarThreadUnsafe(long CondVarAddress)
  359. {
  360. KThread WakeThread = null;
  361. foreach (KThread Thread in Process.ThreadArbiterList)
  362. {
  363. if (Thread.CondVarAddress != CondVarAddress)
  364. {
  365. continue;
  366. }
  367. if (WakeThread == null || Thread.ActualPriority < WakeThread.ActualPriority)
  368. {
  369. WakeThread = Thread;
  370. }
  371. }
  372. if (WakeThread != null)
  373. {
  374. Process.ThreadArbiterList.Remove(WakeThread);
  375. }
  376. return WakeThread;
  377. }
  378. private bool IsPointingInsideKernel(long Address)
  379. {
  380. return ((ulong)Address + 0x1000000000) < 0xffffff000;
  381. }
  382. private bool IsWordAddressUnaligned(long Address)
  383. {
  384. return (Address & 3) != 0;
  385. }
  386. }
  387. }