SvcThreadSync.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  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. Logger.PrintDebug(LogClass.KernelSvc, $"Sync handle 0x{Handle:x8}");
  28. KSynchronizationObject SyncObj = Process.HandleTable.GetObject<KSynchronizationObject>(Handle);
  29. if (SyncObj == null)
  30. {
  31. break;
  32. }
  33. SyncObjs.Add(SyncObj);
  34. }
  35. int HndIndex = (int)ThreadState.X1;
  36. ulong High = ThreadState.X1 & (0xffffffffUL << 32);
  37. long Result = System.Synchronization.WaitFor(SyncObjs.ToArray(), Timeout, ref HndIndex);
  38. if (Result != 0)
  39. {
  40. if (Result == MakeError(ErrorModule.Kernel, KernelErr.Timeout) ||
  41. Result == MakeError(ErrorModule.Kernel, KernelErr.Cancelled))
  42. {
  43. Logger.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
  44. }
  45. else
  46. {
  47. Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
  48. }
  49. }
  50. ThreadState.X0 = (ulong)Result;
  51. ThreadState.X1 = (uint)HndIndex | High;
  52. }
  53. private void SvcCancelSynchronization(CpuThreadState ThreadState)
  54. {
  55. int ThreadHandle = (int)ThreadState.X0;
  56. Logger.PrintDebug(LogClass.KernelSvc, "ThreadHandle = 0x" + ThreadHandle.ToString("x8"));
  57. KThread Thread = Process.HandleTable.GetKThread(ThreadHandle);
  58. if (Thread == null)
  59. {
  60. Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!");
  61. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
  62. return;
  63. }
  64. Thread.CancelSynchronization();
  65. ThreadState.X0 = 0;
  66. }
  67. private void SvcArbitrateLock(CpuThreadState ThreadState)
  68. {
  69. int OwnerHandle = (int)ThreadState.X0;
  70. long MutexAddress = (long)ThreadState.X1;
  71. int RequesterHandle = (int)ThreadState.X2;
  72. Logger.PrintDebug(LogClass.KernelSvc,
  73. "OwnerHandle = 0x" + OwnerHandle .ToString("x8") + ", " +
  74. "MutexAddress = 0x" + MutexAddress .ToString("x16") + ", " +
  75. "RequesterHandle = 0x" + RequesterHandle.ToString("x8"));
  76. if (IsPointingInsideKernel(MutexAddress))
  77. {
  78. Logger.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
  79. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
  80. return;
  81. }
  82. if (IsAddressNotWordAligned(MutexAddress))
  83. {
  84. Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
  85. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
  86. return;
  87. }
  88. KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
  89. long Result = CurrentProcess.AddressArbiter.ArbitrateLock(OwnerHandle, MutexAddress, RequesterHandle);
  90. if (Result != 0)
  91. {
  92. Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
  93. }
  94. ThreadState.X0 = (ulong)Result;
  95. }
  96. private void SvcArbitrateUnlock(CpuThreadState ThreadState)
  97. {
  98. long MutexAddress = (long)ThreadState.X0;
  99. Logger.PrintDebug(LogClass.KernelSvc, "MutexAddress = 0x" + MutexAddress.ToString("x16"));
  100. if (IsPointingInsideKernel(MutexAddress))
  101. {
  102. Logger.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
  103. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
  104. return;
  105. }
  106. if (IsAddressNotWordAligned(MutexAddress))
  107. {
  108. Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
  109. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
  110. return;
  111. }
  112. KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
  113. long Result = CurrentProcess.AddressArbiter.ArbitrateUnlock(MutexAddress);
  114. if (Result != 0)
  115. {
  116. Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
  117. }
  118. ThreadState.X0 = (ulong)Result;
  119. }
  120. private void SvcWaitProcessWideKeyAtomic(CpuThreadState ThreadState)
  121. {
  122. long MutexAddress = (long)ThreadState.X0;
  123. long CondVarAddress = (long)ThreadState.X1;
  124. int ThreadHandle = (int)ThreadState.X2;
  125. long Timeout = (long)ThreadState.X3;
  126. Logger.PrintDebug(LogClass.KernelSvc,
  127. "MutexAddress = 0x" + MutexAddress .ToString("x16") + ", " +
  128. "CondVarAddress = 0x" + CondVarAddress.ToString("x16") + ", " +
  129. "ThreadHandle = 0x" + ThreadHandle .ToString("x8") + ", " +
  130. "Timeout = 0x" + Timeout .ToString("x16"));
  131. if (IsPointingInsideKernel(MutexAddress))
  132. {
  133. Logger.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
  134. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
  135. return;
  136. }
  137. if (IsAddressNotWordAligned(MutexAddress))
  138. {
  139. Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
  140. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
  141. return;
  142. }
  143. KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
  144. long Result = CurrentProcess.AddressArbiter.WaitProcessWideKeyAtomic(
  145. MutexAddress,
  146. CondVarAddress,
  147. ThreadHandle,
  148. Timeout);
  149. if (Result != 0)
  150. {
  151. if (Result == MakeError(ErrorModule.Kernel, KernelErr.Timeout))
  152. {
  153. Logger.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
  154. }
  155. else
  156. {
  157. Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
  158. }
  159. }
  160. ThreadState.X0 = (ulong)Result;
  161. }
  162. private void SvcSignalProcessWideKey(CpuThreadState ThreadState)
  163. {
  164. long Address = (long)ThreadState.X0;
  165. int Count = (int)ThreadState.X1;
  166. Logger.PrintDebug(LogClass.KernelSvc,
  167. "Address = 0x" + Address.ToString("x16") + ", " +
  168. "Count = 0x" + Count .ToString("x8"));
  169. KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
  170. CurrentProcess.AddressArbiter.SignalProcessWideKey(Address, Count);
  171. ThreadState.X0 = 0;
  172. }
  173. private void SvcWaitForAddress(CpuThreadState ThreadState)
  174. {
  175. long Address = (long)ThreadState.X0;
  176. ArbitrationType Type = (ArbitrationType)ThreadState.X1;
  177. int Value = (int)ThreadState.X2;
  178. long Timeout = (long)ThreadState.X3;
  179. Logger.PrintDebug(LogClass.KernelSvc,
  180. "Address = 0x" + Address.ToString("x16") + ", " +
  181. "Type = " + Type .ToString() + ", " +
  182. "Value = 0x" + Value .ToString("x8") + ", " +
  183. "Timeout = 0x" + Timeout.ToString("x16"));
  184. if (IsPointingInsideKernel(Address))
  185. {
  186. Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!");
  187. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
  188. return;
  189. }
  190. if (IsAddressNotWordAligned(Address))
  191. {
  192. Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!");
  193. ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
  194. return;
  195. }
  196. KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
  197. long Result;
  198. switch (Type)
  199. {
  200. case ArbitrationType.WaitIfLessThan:
  201. Result = CurrentProcess.AddressArbiter.WaitForAddressIfLessThan(Address, Value, false, Timeout);
  202. break;
  203. case ArbitrationType.DecrementAndWaitIfLessThan:
  204. Result = CurrentProcess.AddressArbiter.WaitForAddressIfLessThan(Address, Value, true, Timeout);
  205. break;
  206. case ArbitrationType.WaitIfEqual:
  207. Result = CurrentProcess.AddressArbiter.WaitForAddressIfEqual(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. KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
  243. long Result;
  244. switch (Type)
  245. {
  246. case SignalType.Signal:
  247. Result = CurrentProcess.AddressArbiter.Signal(Address, Count);
  248. break;
  249. case SignalType.SignalAndIncrementIfEqual:
  250. Result = CurrentProcess.AddressArbiter.SignalAndIncrementIfEqual(Address, Value, Count);
  251. break;
  252. case SignalType.SignalAndModifyIfEqual:
  253. Result = CurrentProcess.AddressArbiter.SignalAndModifyIfEqual(Address, Value, Count);
  254. break;
  255. default:
  256. Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
  257. break;
  258. }
  259. if (Result != 0)
  260. {
  261. Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
  262. }
  263. ThreadState.X0 = (ulong)Result;
  264. }
  265. private bool IsPointingInsideKernel(long Address)
  266. {
  267. return ((ulong)Address + 0x1000000000) < 0xffffff000;
  268. }
  269. private bool IsAddressNotWordAligned(long Address)
  270. {
  271. return (Address & 3) != 0;
  272. }
  273. }
  274. }