SvcThreadSync.cs 13 KB

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