SvcThreadSync.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. using ChocolArm64.State;
  2. using Ryujinx.HLE.Logging;
  3. using static Ryujinx.HLE.HOS.ErrorCode;
  4. namespace Ryujinx.HLE.HOS.Kernel
  5. {
  6. partial class SvcHandler
  7. {
  8. private void SvcWaitSynchronization(AThreadState ThreadState)
  9. {
  10. long HandlesPtr = (long)ThreadState.X1;
  11. int HandlesCount = (int)ThreadState.X2;
  12. long Timeout = (long)ThreadState.X3;
  13. Device.Log.PrintDebug(LogClass.KernelSvc,
  14. "HandlesPtr = 0x" + HandlesPtr .ToString("x16") + ", " +
  15. "HandlesCount = 0x" + HandlesCount.ToString("x8") + ", " +
  16. "Timeout = 0x" + Timeout .ToString("x16"));
  17. if ((uint)HandlesCount > 0x40)
  18. {
  19. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.CountOutOfRange);
  20. return;
  21. }
  22. KSynchronizationObject[] SyncObjs = new KSynchronizationObject[HandlesCount];
  23. for (int Index = 0; Index < HandlesCount; Index++)
  24. {
  25. int Handle = Memory.ReadInt32(HandlesPtr + Index * 4);
  26. KSynchronizationObject SyncObj = Process.HandleTable.GetData<KSynchronizationObject>(Handle);
  27. SyncObjs[Index] = SyncObj;
  28. }
  29. int HndIndex = (int)ThreadState.X1;
  30. ulong High = ThreadState.X1 & (0xffffffffUL << 32);
  31. long Result = System.Synchronization.WaitFor(SyncObjs, Timeout, ref HndIndex);
  32. if (Result != 0)
  33. {
  34. if (Result == MakeError(ErrorModule.Kernel, KernelErr.Timeout) ||
  35. Result == MakeError(ErrorModule.Kernel, KernelErr.Cancelled))
  36. {
  37. Device.Log.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
  38. }
  39. else
  40. {
  41. Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
  42. }
  43. }
  44. ThreadState.X0 = (ulong)Result;
  45. ThreadState.X1 = (uint)HndIndex | High;
  46. }
  47. private void SvcCancelSynchronization(AThreadState ThreadState)
  48. {
  49. int ThreadHandle = (int)ThreadState.X0;
  50. Device.Log.PrintDebug(LogClass.KernelSvc, "ThreadHandle = 0x" + ThreadHandle.ToString("x8"));
  51. KThread Thread = Process.HandleTable.GetData<KThread>(ThreadHandle);
  52. if (Thread == null)
  53. {
  54. Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!");
  55. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
  56. return;
  57. }
  58. Thread.CancelSynchronization();
  59. ThreadState.X0 = 0;
  60. }
  61. private void SvcArbitrateLock(AThreadState ThreadState)
  62. {
  63. int OwnerHandle = (int)ThreadState.X0;
  64. long MutexAddress = (long)ThreadState.X1;
  65. int RequesterHandle = (int)ThreadState.X2;
  66. Device.Log.PrintDebug(LogClass.KernelSvc,
  67. "OwnerHandle = 0x" + OwnerHandle .ToString("x8") + ", " +
  68. "MutexAddress = 0x" + MutexAddress .ToString("x16") + ", " +
  69. "RequesterHandle = 0x" + RequesterHandle.ToString("x8"));
  70. if (IsPointingInsideKernel(MutexAddress))
  71. {
  72. Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
  73. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
  74. return;
  75. }
  76. if (IsAddressNotWordAligned(MutexAddress))
  77. {
  78. Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
  79. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
  80. return;
  81. }
  82. long Result = System.AddressArbiter.ArbitrateLock(
  83. Process,
  84. Memory,
  85. OwnerHandle,
  86. MutexAddress,
  87. RequesterHandle);
  88. if (Result != 0)
  89. {
  90. Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
  91. }
  92. ThreadState.X0 = (ulong)Result;
  93. }
  94. private void SvcArbitrateUnlock(AThreadState ThreadState)
  95. {
  96. long MutexAddress = (long)ThreadState.X0;
  97. Device.Log.PrintDebug(LogClass.KernelSvc, "MutexAddress = 0x" + MutexAddress.ToString("x16"));
  98. if (IsPointingInsideKernel(MutexAddress))
  99. {
  100. Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
  101. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
  102. return;
  103. }
  104. if (IsAddressNotWordAligned(MutexAddress))
  105. {
  106. Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
  107. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
  108. return;
  109. }
  110. long Result = System.AddressArbiter.ArbitrateUnlock(Memory, MutexAddress);
  111. if (Result != 0)
  112. {
  113. Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
  114. }
  115. ThreadState.X0 = (ulong)Result;
  116. }
  117. private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState)
  118. {
  119. long MutexAddress = (long)ThreadState.X0;
  120. long CondVarAddress = (long)ThreadState.X1;
  121. int ThreadHandle = (int)ThreadState.X2;
  122. long Timeout = (long)ThreadState.X3;
  123. Device.Log.PrintDebug(LogClass.KernelSvc,
  124. "MutexAddress = 0x" + MutexAddress .ToString("x16") + ", " +
  125. "CondVarAddress = 0x" + CondVarAddress.ToString("x16") + ", " +
  126. "ThreadHandle = 0x" + ThreadHandle .ToString("x8") + ", " +
  127. "Timeout = 0x" + Timeout .ToString("x16"));
  128. if (IsPointingInsideKernel(MutexAddress))
  129. {
  130. Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
  131. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
  132. return;
  133. }
  134. if (IsAddressNotWordAligned(MutexAddress))
  135. {
  136. Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
  137. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
  138. return;
  139. }
  140. long Result = System.AddressArbiter.WaitProcessWideKeyAtomic(
  141. Memory,
  142. MutexAddress,
  143. CondVarAddress,
  144. ThreadHandle,
  145. Timeout);
  146. if (Result != 0)
  147. {
  148. if (Result == MakeError(ErrorModule.Kernel, KernelErr.Timeout))
  149. {
  150. Device.Log.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
  151. }
  152. else
  153. {
  154. Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
  155. }
  156. }
  157. ThreadState.X0 = (ulong)Result;
  158. }
  159. private void SvcSignalProcessWideKey(AThreadState ThreadState)
  160. {
  161. long Address = (long)ThreadState.X0;
  162. int Count = (int)ThreadState.X1;
  163. Device.Log.PrintDebug(LogClass.KernelSvc,
  164. "Address = 0x" + Address.ToString("x16") + ", " +
  165. "Count = 0x" + Count .ToString("x8"));
  166. System.AddressArbiter.SignalProcessWideKey(Process, Memory, Address, Count);
  167. ThreadState.X0 = 0;
  168. }
  169. private void SvcWaitForAddress(AThreadState ThreadState)
  170. {
  171. long Address = (long)ThreadState.X0;
  172. ArbitrationType Type = (ArbitrationType)ThreadState.X1;
  173. int Value = (int)ThreadState.X2;
  174. long Timeout = (long)ThreadState.X3;
  175. Device.Log.PrintDebug(LogClass.KernelSvc,
  176. "Address = 0x" + Address.ToString("x16") + ", " +
  177. "Type = " + Type .ToString() + ", " +
  178. "Value = 0x" + Value .ToString("x8") + ", " +
  179. "Timeout = 0x" + Timeout.ToString("x16"));
  180. if (IsPointingInsideKernel(Address))
  181. {
  182. Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!");
  183. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
  184. return;
  185. }
  186. if (IsAddressNotWordAligned(Address))
  187. {
  188. Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!");
  189. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
  190. return;
  191. }
  192. long Result;
  193. switch (Type)
  194. {
  195. case ArbitrationType.WaitIfLessThan:
  196. Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, false, Timeout);
  197. break;
  198. case ArbitrationType.DecrementAndWaitIfLessThan:
  199. Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, true, Timeout);
  200. break;
  201. case ArbitrationType.WaitIfEqual:
  202. Result = System.AddressArbiter.WaitForAddressIfEqual(Memory, Address, Value, Timeout);
  203. break;
  204. default:
  205. Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
  206. break;
  207. }
  208. if (Result != 0)
  209. {
  210. Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
  211. }
  212. ThreadState.X0 = (ulong)Result;
  213. }
  214. private void SvcSignalToAddress(AThreadState ThreadState)
  215. {
  216. long Address = (long)ThreadState.X0;
  217. SignalType Type = (SignalType)ThreadState.X1;
  218. int Value = (int)ThreadState.X2;
  219. int Count = (int)ThreadState.X3;
  220. Device.Log.PrintDebug(LogClass.KernelSvc,
  221. "Address = 0x" + Address.ToString("x16") + ", " +
  222. "Type = " + Type .ToString() + ", " +
  223. "Value = 0x" + Value .ToString("x8") + ", " +
  224. "Count = 0x" + Count .ToString("x8"));
  225. if (IsPointingInsideKernel(Address))
  226. {
  227. Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!");
  228. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
  229. return;
  230. }
  231. if (IsAddressNotWordAligned(Address))
  232. {
  233. Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!");
  234. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
  235. return;
  236. }
  237. long Result;
  238. switch (Type)
  239. {
  240. case SignalType.Signal:
  241. Result = System.AddressArbiter.Signal(Address, Count);
  242. break;
  243. case SignalType.SignalAndIncrementIfEqual:
  244. Result = System.AddressArbiter.SignalAndIncrementIfEqual(Memory, Address, Value, Count);
  245. break;
  246. case SignalType.SignalAndModifyIfEqual:
  247. Result = System.AddressArbiter.SignalAndModifyIfEqual(Memory, Address, Value, Count);
  248. break;
  249. default:
  250. Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
  251. break;
  252. }
  253. if (Result != 0)
  254. {
  255. Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
  256. }
  257. ThreadState.X0 = (ulong)Result;
  258. }
  259. private bool IsPointingInsideKernel(long Address)
  260. {
  261. return ((ulong)Address + 0x1000000000) < 0xffffff000;
  262. }
  263. private bool IsAddressNotWordAligned(long Address)
  264. {
  265. return (Address & 3) != 0;
  266. }
  267. }
  268. }