NvHostCtrlDeviceFile.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Graphics.Gpu.Synchronization;
  3. using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl.Types;
  4. using Ryujinx.HLE.HOS.Services.Nv.Types;
  5. using Ryujinx.HLE.HOS.Services.Settings;
  6. using Ryujinx.Memory;
  7. using System;
  8. using System.Text;
  9. using System.Threading;
  10. namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
  11. {
  12. internal class NvHostCtrlDeviceFile : NvDeviceFile
  13. {
  14. public const int EventsCount = 64;
  15. private bool _isProductionMode;
  16. private Switch _device;
  17. private NvHostEvent[] _events;
  18. public NvHostCtrlDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, ulong owner) : base(context, owner)
  19. {
  20. if (NxSettings.Settings.TryGetValue("nv!rmos_set_production_mode", out object productionModeSetting))
  21. {
  22. _isProductionMode = ((string)productionModeSetting) != "0"; // Default value is ""
  23. }
  24. else
  25. {
  26. _isProductionMode = true;
  27. }
  28. _device = context.Device;
  29. _events = new NvHostEvent[EventsCount];
  30. }
  31. public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments)
  32. {
  33. NvInternalResult result = NvInternalResult.NotImplemented;
  34. if (command.Type == NvIoctl.NvHostCustomMagic)
  35. {
  36. switch (command.Number)
  37. {
  38. case 0x14:
  39. result = CallIoctlMethod<NvFence>(SyncptRead, arguments);
  40. break;
  41. case 0x15:
  42. result = CallIoctlMethod<uint>(SyncptIncr, arguments);
  43. break;
  44. case 0x16:
  45. result = CallIoctlMethod<SyncptWaitArguments>(SyncptWait, arguments);
  46. break;
  47. case 0x19:
  48. result = CallIoctlMethod<SyncptWaitExArguments>(SyncptWaitEx, arguments);
  49. break;
  50. case 0x1a:
  51. result = CallIoctlMethod<NvFence>(SyncptReadMax, arguments);
  52. break;
  53. case 0x1b:
  54. // As Marshal cannot handle unaligned arrays, we do everything by hand here.
  55. GetConfigurationArguments configArgument = GetConfigurationArguments.FromSpan(arguments);
  56. result = GetConfig(configArgument);
  57. if (result == NvInternalResult.Success)
  58. {
  59. configArgument.CopyTo(arguments);
  60. }
  61. break;
  62. case 0x1c:
  63. result = CallIoctlMethod<uint>(EventSignal, arguments);
  64. break;
  65. case 0x1d:
  66. result = CallIoctlMethod<EventWaitArguments>(EventWait, arguments);
  67. break;
  68. case 0x1e:
  69. result = CallIoctlMethod<EventWaitArguments>(EventWaitAsync, arguments);
  70. break;
  71. case 0x1f:
  72. result = CallIoctlMethod<uint>(EventRegister, arguments);
  73. break;
  74. case 0x20:
  75. result = CallIoctlMethod<uint>(EventUnregister, arguments);
  76. break;
  77. case 0x21:
  78. result = CallIoctlMethod<ulong>(EventKill, arguments);
  79. break;
  80. }
  81. }
  82. return result;
  83. }
  84. private int QueryEvent(uint eventId)
  85. {
  86. lock (_events)
  87. {
  88. uint eventSlot;
  89. uint syncpointId;
  90. if ((eventId >> 28) == 1)
  91. {
  92. eventSlot = eventId & 0xFFFF;
  93. syncpointId = (eventId >> 16) & 0xFFF;
  94. }
  95. else
  96. {
  97. eventSlot = eventId & 0xFF;
  98. syncpointId = eventId >> 4;
  99. }
  100. if (eventSlot >= EventsCount || _events[eventSlot] == null || _events[eventSlot].Fence.Id != syncpointId)
  101. {
  102. return 0;
  103. }
  104. return _events[eventSlot].EventHandle;
  105. }
  106. }
  107. public override NvInternalResult QueryEvent(out int eventHandle, uint eventId)
  108. {
  109. eventHandle = QueryEvent(eventId);
  110. return eventHandle != 0 ? NvInternalResult.Success : NvInternalResult.InvalidInput;
  111. }
  112. private NvInternalResult SyncptRead(ref NvFence arguments)
  113. {
  114. return SyncptReadMinOrMax(ref arguments, max: false);
  115. }
  116. private NvInternalResult SyncptIncr(ref uint id)
  117. {
  118. if (id >= SynchronizationManager.MaxHardwareSyncpoints)
  119. {
  120. return NvInternalResult.InvalidInput;
  121. }
  122. _device.System.HostSyncpoint.Increment(id);
  123. return NvInternalResult.Success;
  124. }
  125. private NvInternalResult SyncptWait(ref SyncptWaitArguments arguments)
  126. {
  127. uint dummyValue = 0;
  128. return EventWait(ref arguments.Fence, ref dummyValue, arguments.Timeout, isWaitEventAsyncCmd: false, isWaitEventCmd: false);
  129. }
  130. private NvInternalResult SyncptWaitEx(ref SyncptWaitExArguments arguments)
  131. {
  132. return EventWait(ref arguments.Input.Fence, ref arguments.Value, arguments.Input.Timeout, isWaitEventAsyncCmd: false, isWaitEventCmd: false);
  133. }
  134. private NvInternalResult SyncptReadMax(ref NvFence arguments)
  135. {
  136. return SyncptReadMinOrMax(ref arguments, max: true);
  137. }
  138. private NvInternalResult GetConfig(GetConfigurationArguments arguments)
  139. {
  140. if (!_isProductionMode && NxSettings.Settings.TryGetValue($"{arguments.Domain}!{arguments.Parameter}".ToLower(), out object nvSetting))
  141. {
  142. byte[] settingBuffer = new byte[0x101];
  143. if (nvSetting is string stringValue)
  144. {
  145. if (stringValue.Length > 0x100)
  146. {
  147. Logger.Error?.Print(LogClass.ServiceNv, $"{arguments.Domain}!{arguments.Parameter} String value size is too big!");
  148. }
  149. else
  150. {
  151. settingBuffer = Encoding.ASCII.GetBytes(stringValue + "\0");
  152. }
  153. }
  154. else if (nvSetting is int intValue)
  155. {
  156. settingBuffer = BitConverter.GetBytes(intValue);
  157. }
  158. else if (nvSetting is bool boolValue)
  159. {
  160. settingBuffer[0] = boolValue ? (byte)1 : (byte)0;
  161. }
  162. else
  163. {
  164. throw new NotImplementedException(nvSetting.GetType().Name);
  165. }
  166. Logger.Debug?.Print(LogClass.ServiceNv, $"Got setting {arguments.Domain}!{arguments.Parameter}");
  167. arguments.Configuration = settingBuffer;
  168. return NvInternalResult.Success;
  169. }
  170. // NOTE: This actually return NotAvailableInProduction but this is directly translated as a InvalidInput before returning the ioctl.
  171. //return NvInternalResult.NotAvailableInProduction;
  172. return NvInternalResult.InvalidInput;
  173. }
  174. private NvInternalResult EventWait(ref EventWaitArguments arguments)
  175. {
  176. return EventWait(ref arguments.Fence, ref arguments.Value, arguments.Timeout, isWaitEventAsyncCmd: false, isWaitEventCmd: true);
  177. }
  178. private NvInternalResult EventWaitAsync(ref EventWaitArguments arguments)
  179. {
  180. return EventWait(ref arguments.Fence, ref arguments.Value, arguments.Timeout, isWaitEventAsyncCmd: true, isWaitEventCmd: false);
  181. }
  182. private NvInternalResult EventRegister(ref uint userEventId)
  183. {
  184. lock (_events)
  185. {
  186. NvInternalResult result = EventUnregister(ref userEventId);
  187. if (result == NvInternalResult.Success)
  188. {
  189. _events[userEventId] = new NvHostEvent(_device.System.HostSyncpoint, userEventId, _device.System);
  190. }
  191. return result;
  192. }
  193. }
  194. private NvInternalResult EventUnregister(ref uint userEventId)
  195. {
  196. lock (_events)
  197. {
  198. if (userEventId >= EventsCount)
  199. {
  200. return NvInternalResult.InvalidInput;
  201. }
  202. NvHostEvent hostEvent = _events[userEventId];
  203. if (hostEvent == null)
  204. {
  205. return NvInternalResult.Success;
  206. }
  207. if (hostEvent.State == NvHostEventState.Available ||
  208. hostEvent.State == NvHostEventState.Cancelled ||
  209. hostEvent.State == NvHostEventState.Signaled)
  210. {
  211. _events[userEventId].CloseEvent(Context);
  212. _events[userEventId] = null;
  213. return NvInternalResult.Success;
  214. }
  215. return NvInternalResult.Busy;
  216. }
  217. }
  218. private NvInternalResult EventKill(ref ulong eventMask)
  219. {
  220. lock (_events)
  221. {
  222. NvInternalResult result = NvInternalResult.Success;
  223. for (uint eventId = 0; eventId < EventsCount; eventId++)
  224. {
  225. if ((eventMask & (1UL << (int)eventId)) != 0)
  226. {
  227. NvInternalResult tmp = EventUnregister(ref eventId);
  228. if (tmp != NvInternalResult.Success)
  229. {
  230. result = tmp;
  231. }
  232. }
  233. }
  234. return result;
  235. }
  236. }
  237. private NvInternalResult EventSignal(ref uint userEventId)
  238. {
  239. uint eventId = userEventId & ushort.MaxValue;
  240. if (eventId >= EventsCount)
  241. {
  242. return NvInternalResult.InvalidInput;
  243. }
  244. lock (_events)
  245. {
  246. NvHostEvent hostEvent = _events[eventId];
  247. if (hostEvent == null)
  248. {
  249. return NvInternalResult.InvalidInput;
  250. }
  251. hostEvent.Cancel(_device.Gpu);
  252. _device.System.HostSyncpoint.UpdateMin(hostEvent.Fence.Id);
  253. return NvInternalResult.Success;
  254. }
  255. }
  256. private NvInternalResult SyncptReadMinOrMax(ref NvFence arguments, bool max)
  257. {
  258. if (arguments.Id >= SynchronizationManager.MaxHardwareSyncpoints)
  259. {
  260. return NvInternalResult.InvalidInput;
  261. }
  262. if (max)
  263. {
  264. arguments.Value = _device.System.HostSyncpoint.ReadSyncpointMaxValue(arguments.Id);
  265. }
  266. else
  267. {
  268. arguments.Value = _device.System.HostSyncpoint.ReadSyncpointValue(arguments.Id);
  269. }
  270. return NvInternalResult.Success;
  271. }
  272. private NvInternalResult EventWait(ref NvFence fence, ref uint value, int timeout, bool isWaitEventAsyncCmd, bool isWaitEventCmd)
  273. {
  274. if (fence.Id >= SynchronizationManager.MaxHardwareSyncpoints)
  275. {
  276. return NvInternalResult.InvalidInput;
  277. }
  278. // First try to check if the syncpoint is already expired on the CPU side
  279. if (_device.System.HostSyncpoint.IsSyncpointExpired(fence.Id, fence.Value))
  280. {
  281. value = _device.System.HostSyncpoint.ReadSyncpointMinValue(fence.Id);
  282. return NvInternalResult.Success;
  283. }
  284. // Try to invalidate the CPU cache and check for expiration again.
  285. uint newCachedSyncpointValue = _device.System.HostSyncpoint.UpdateMin(fence.Id);
  286. // Has the fence already expired?
  287. if (_device.System.HostSyncpoint.IsSyncpointExpired(fence.Id, fence.Value))
  288. {
  289. value = newCachedSyncpointValue;
  290. return NvInternalResult.Success;
  291. }
  292. // If the timeout is 0, directly return.
  293. if (timeout == 0)
  294. {
  295. return NvInternalResult.TryAgain;
  296. }
  297. // The syncpoint value isn't at the fence yet, we need to wait.
  298. if (!isWaitEventAsyncCmd)
  299. {
  300. value = 0;
  301. }
  302. NvHostEvent hostEvent;
  303. NvInternalResult result;
  304. uint eventIndex;
  305. lock (_events)
  306. {
  307. if (isWaitEventAsyncCmd)
  308. {
  309. eventIndex = value;
  310. if (eventIndex >= EventsCount)
  311. {
  312. return NvInternalResult.InvalidInput;
  313. }
  314. hostEvent = _events[eventIndex];
  315. }
  316. else
  317. {
  318. hostEvent = GetFreeEventLocked(fence.Id, out eventIndex);
  319. }
  320. if (hostEvent != null)
  321. {
  322. lock (hostEvent.Lock)
  323. {
  324. if (hostEvent.State == NvHostEventState.Available ||
  325. hostEvent.State == NvHostEventState.Signaled ||
  326. hostEvent.State == NvHostEventState.Cancelled)
  327. {
  328. bool timedOut = hostEvent.Wait(_device.Gpu, fence);
  329. if (timedOut)
  330. {
  331. if (isWaitEventCmd)
  332. {
  333. value = ((fence.Id & 0xfff) << 16) | 0x10000000;
  334. }
  335. else
  336. {
  337. value = fence.Id << 4;
  338. }
  339. value |= eventIndex;
  340. result = NvInternalResult.TryAgain;
  341. }
  342. else
  343. {
  344. value = fence.Value;
  345. return NvInternalResult.Success;
  346. }
  347. }
  348. else
  349. {
  350. Logger.Error?.Print(LogClass.ServiceNv, $"Invalid Event at index {eventIndex} (isWaitEventAsyncCmd: {isWaitEventAsyncCmd}, isWaitEventCmd: {isWaitEventCmd})");
  351. if (hostEvent != null)
  352. {
  353. Logger.Error?.Print(LogClass.ServiceNv, hostEvent.DumpState(_device.Gpu));
  354. }
  355. result = NvInternalResult.InvalidInput;
  356. }
  357. }
  358. }
  359. else
  360. {
  361. Logger.Error?.Print(LogClass.ServiceNv, $"Invalid Event at index {eventIndex} (isWaitEventAsyncCmd: {isWaitEventAsyncCmd}, isWaitEventCmd: {isWaitEventCmd})");
  362. result = NvInternalResult.InvalidInput;
  363. }
  364. }
  365. return result;
  366. }
  367. private NvHostEvent GetFreeEventLocked(uint id, out uint eventIndex)
  368. {
  369. eventIndex = EventsCount;
  370. uint nullIndex = EventsCount;
  371. for (uint index = 0; index < EventsCount; index++)
  372. {
  373. NvHostEvent Event = _events[index];
  374. if (Event != null)
  375. {
  376. if (Event.State == NvHostEventState.Available ||
  377. Event.State == NvHostEventState.Signaled ||
  378. Event.State == NvHostEventState.Cancelled)
  379. {
  380. eventIndex = index;
  381. if (Event.Fence.Id == id)
  382. {
  383. return Event;
  384. }
  385. }
  386. }
  387. else if (nullIndex == EventsCount)
  388. {
  389. nullIndex = index;
  390. }
  391. }
  392. if (nullIndex < EventsCount)
  393. {
  394. eventIndex = nullIndex;
  395. EventRegister(ref eventIndex);
  396. return _events[nullIndex];
  397. }
  398. if (eventIndex < EventsCount)
  399. {
  400. return _events[eventIndex];
  401. }
  402. return null;
  403. }
  404. public override void Close()
  405. {
  406. Logger.Warning?.Print(LogClass.ServiceNv, "Closing channel");
  407. lock (_events)
  408. {
  409. // If the device file need to be closed, cancel all user events and dispose events.
  410. for (int i = 0; i < _events.Length; i++)
  411. {
  412. NvHostEvent evnt = _events[i];
  413. if (evnt != null)
  414. {
  415. lock (evnt.Lock)
  416. {
  417. if (evnt.State == NvHostEventState.Waiting)
  418. {
  419. evnt.State = NvHostEventState.Cancelling;
  420. evnt.Cancel(_device.Gpu);
  421. }
  422. else if (evnt.State == NvHostEventState.Signaling)
  423. {
  424. // Wait at max 9ms if the guest app is trying to signal the event while closing it..
  425. int retryCount = 0;
  426. do
  427. {
  428. if (retryCount++ > 9)
  429. {
  430. break;
  431. }
  432. // TODO: This should be handled by the kernel (reschedule the current thread ect), waiting for Kernel decoupling work.
  433. Thread.Sleep(1);
  434. } while (evnt.State != NvHostEventState.Signaled);
  435. }
  436. evnt.CloseEvent(Context);
  437. _events[i] = null;
  438. }
  439. }
  440. }
  441. }
  442. }
  443. }
  444. }