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. }