SvcThreadSync.cs 14 KB

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