NvHostCtrlDeviceFile.cs 19 KB

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