NvHostChannelDeviceFile.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Graphics.Gpu;
  3. using Ryujinx.Graphics.Gpu.Memory;
  4. using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types;
  5. using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
  6. using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
  7. using Ryujinx.HLE.HOS.Services.Nv.Types;
  8. using Ryujinx.Memory;
  9. using System;
  10. using System.Collections.Concurrent;
  11. using System.Runtime.CompilerServices;
  12. using System.Runtime.InteropServices;
  13. namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
  14. {
  15. class NvHostChannelDeviceFile : NvDeviceFile
  16. {
  17. private static readonly ConcurrentDictionary<long, Host1xContext> _host1xContextRegistry = new();
  18. private const uint MaxModuleSyncpoint = 16;
  19. private uint _timeout;
  20. private uint _submitTimeout;
  21. private uint _timeslice;
  22. private readonly Switch _device;
  23. private readonly IVirtualMemoryManager _memory;
  24. private readonly Host1xContext _host1xContext;
  25. private readonly long _contextId;
  26. public GpuChannel Channel { get; }
  27. public enum ResourcePolicy
  28. {
  29. Device,
  30. Channel
  31. }
  32. protected static uint[] DeviceSyncpoints = new uint[MaxModuleSyncpoint];
  33. protected uint[] ChannelSyncpoints;
  34. protected static ResourcePolicy ChannelResourcePolicy = ResourcePolicy.Device;
  35. private NvFence _channelSyncpoint;
  36. public NvHostChannelDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner)
  37. {
  38. _device = context.Device;
  39. _memory = memory;
  40. _timeout = 3000;
  41. _submitTimeout = 0;
  42. _timeslice = 0;
  43. _host1xContext = GetHost1XContext(context.Device.Gpu, owner);
  44. _contextId = _host1xContext.Host1x.CreateContext();
  45. Channel = _device.Gpu.CreateChannel();
  46. ChannelInitialization.InitializeState(Channel);
  47. ChannelSyncpoints = new uint[MaxModuleSyncpoint];
  48. _channelSyncpoint.Id = _device.System.HostSyncpoint.AllocateSyncpoint(false);
  49. _channelSyncpoint.UpdateValue(_device.System.HostSyncpoint);
  50. }
  51. public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments)
  52. {
  53. NvInternalResult result = NvInternalResult.NotImplemented;
  54. if (command.Type == NvIoctl.NvHostCustomMagic)
  55. {
  56. switch (command.Number)
  57. {
  58. case 0x01:
  59. result = Submit(arguments);
  60. break;
  61. case 0x02:
  62. result = CallIoctlMethod<GetParameterArguments>(GetSyncpoint, arguments);
  63. break;
  64. case 0x03:
  65. result = CallIoctlMethod<GetParameterArguments>(GetWaitBase, arguments);
  66. break;
  67. case 0x07:
  68. result = CallIoctlMethod<uint>(SetSubmitTimeout, arguments);
  69. break;
  70. case 0x09:
  71. result = MapCommandBuffer(arguments);
  72. break;
  73. case 0x0a:
  74. result = UnmapCommandBuffer(arguments);
  75. break;
  76. }
  77. }
  78. else if (command.Type == NvIoctl.NvHostMagic)
  79. {
  80. switch (command.Number)
  81. {
  82. case 0x01:
  83. result = CallIoctlMethod<int>(SetNvMapFd, arguments);
  84. break;
  85. case 0x03:
  86. result = CallIoctlMethod<uint>(SetTimeout, arguments);
  87. break;
  88. case 0x08:
  89. result = SubmitGpfifo(arguments);
  90. break;
  91. case 0x09:
  92. result = CallIoctlMethod<AllocObjCtxArguments>(AllocObjCtx, arguments);
  93. break;
  94. case 0x0b:
  95. result = CallIoctlMethod<ZcullBindArguments>(ZcullBind, arguments);
  96. break;
  97. case 0x0c:
  98. result = CallIoctlMethod<SetErrorNotifierArguments>(SetErrorNotifier, arguments);
  99. break;
  100. case 0x0d:
  101. result = CallIoctlMethod<NvChannelPriority>(SetPriority, arguments);
  102. break;
  103. case 0x18:
  104. result = CallIoctlMethod<AllocGpfifoExArguments>(AllocGpfifoEx, arguments);
  105. break;
  106. case 0x1a:
  107. result = CallIoctlMethod<AllocGpfifoExArguments>(AllocGpfifoEx2, arguments);
  108. break;
  109. case 0x1d:
  110. result = CallIoctlMethod<uint>(SetTimeslice, arguments);
  111. break;
  112. }
  113. }
  114. else if (command.Type == NvIoctl.NvGpuMagic)
  115. {
  116. switch (command.Number)
  117. {
  118. case 0x14:
  119. result = CallIoctlMethod<ulong>(SetUserData, arguments);
  120. break;
  121. }
  122. }
  123. return result;
  124. }
  125. private NvInternalResult Submit(Span<byte> arguments)
  126. {
  127. SubmitArguments submitHeader = GetSpanAndSkip<SubmitArguments>(ref arguments, 1)[0];
  128. Span<CommandBuffer> commandBuffers = GetSpanAndSkip<CommandBuffer>(ref arguments, submitHeader.CmdBufsCount);
  129. Span<Reloc> relocs = GetSpanAndSkip<Reloc>(ref arguments, submitHeader.RelocsCount);
  130. Span<uint> relocShifts = GetSpanAndSkip<uint>(ref arguments, submitHeader.RelocsCount);
  131. Span<SyncptIncr> syncptIncrs = GetSpanAndSkip<SyncptIncr>(ref arguments, submitHeader.SyncptIncrsCount);
  132. Span<SyncptIncr> waitChecks = GetSpanAndSkip<SyncptIncr>(ref arguments, submitHeader.SyncptIncrsCount); // ?
  133. Span<Fence> fences = GetSpanAndSkip<Fence>(ref arguments, submitHeader.FencesCount);
  134. lock (_device)
  135. {
  136. for (int i = 0; i < syncptIncrs.Length; i++)
  137. {
  138. SyncptIncr syncptIncr = syncptIncrs[i];
  139. uint id = syncptIncr.Id;
  140. fences[i].Id = id;
  141. fences[i].Thresh = Context.Device.System.HostSyncpoint.IncrementSyncpointMax(id, syncptIncr.Incrs);
  142. }
  143. foreach (CommandBuffer commandBuffer in commandBuffers)
  144. {
  145. NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBuffer.Mem);
  146. var data = _memory.GetSpan(map.Address + commandBuffer.Offset, commandBuffer.WordsCount * 4);
  147. _host1xContext.Host1x.Submit(MemoryMarshal.Cast<byte, int>(data), _contextId);
  148. }
  149. }
  150. fences[0].Thresh = Context.Device.System.HostSyncpoint.IncrementSyncpointMax(fences[0].Id, 1);
  151. Span<int> tmpCmdBuff = stackalloc int[1];
  152. tmpCmdBuff[0] = (4 << 28) | (int)fences[0].Id;
  153. _host1xContext.Host1x.Submit(tmpCmdBuff, _contextId);
  154. return NvInternalResult.Success;
  155. }
  156. private Span<T> GetSpanAndSkip<T>(ref Span<byte> arguments, int count) where T : unmanaged
  157. {
  158. Span<T> output = MemoryMarshal.Cast<byte, T>(arguments).Slice(0, count);
  159. arguments = arguments.Slice(Unsafe.SizeOf<T>() * count);
  160. return output;
  161. }
  162. private NvInternalResult GetSyncpoint(ref GetParameterArguments arguments)
  163. {
  164. if (arguments.Parameter >= MaxModuleSyncpoint)
  165. {
  166. return NvInternalResult.InvalidInput;
  167. }
  168. if (ChannelResourcePolicy == ResourcePolicy.Device)
  169. {
  170. arguments.Value = GetSyncpointDevice(_device.System.HostSyncpoint, arguments.Parameter, false);
  171. }
  172. else
  173. {
  174. arguments.Value = GetSyncpointChannel(arguments.Parameter, false);
  175. }
  176. if (arguments.Value == 0)
  177. {
  178. return NvInternalResult.TryAgain;
  179. }
  180. return NvInternalResult.Success;
  181. }
  182. private NvInternalResult GetWaitBase(ref GetParameterArguments arguments)
  183. {
  184. arguments.Value = 0;
  185. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  186. return NvInternalResult.Success;
  187. }
  188. private NvInternalResult SetSubmitTimeout(ref uint submitTimeout)
  189. {
  190. _submitTimeout = submitTimeout;
  191. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  192. return NvInternalResult.Success;
  193. }
  194. private NvInternalResult MapCommandBuffer(Span<byte> arguments)
  195. {
  196. int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
  197. MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
  198. Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries);
  199. foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
  200. {
  201. NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle);
  202. if (map == null)
  203. {
  204. Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!");
  205. return NvInternalResult.InvalidInput;
  206. }
  207. lock (map)
  208. {
  209. if (map.DmaMapAddress == 0)
  210. {
  211. ulong va = _host1xContext.MemoryAllocator.GetFreeAddress((ulong)map.Size, out ulong freeAddressStartPosition, 1, MemoryManager.PageSize);
  212. if (va != NvMemoryAllocator.PteUnmapped && va <= uint.MaxValue && (va + (uint)map.Size) <= uint.MaxValue)
  213. {
  214. _host1xContext.MemoryAllocator.AllocateRange(va, (uint)map.Size, freeAddressStartPosition);
  215. _host1xContext.Smmu.Map(map.Address, va, (uint)map.Size);
  216. map.DmaMapAddress = va;
  217. }
  218. else
  219. {
  220. map.DmaMapAddress = NvMemoryAllocator.PteUnmapped;
  221. }
  222. }
  223. commandBufferEntry.MapAddress = (int)map.DmaMapAddress;
  224. }
  225. }
  226. return NvInternalResult.Success;
  227. }
  228. private NvInternalResult UnmapCommandBuffer(Span<byte> arguments)
  229. {
  230. int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
  231. MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
  232. Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries);
  233. foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
  234. {
  235. NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle);
  236. if (map == null)
  237. {
  238. Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!");
  239. return NvInternalResult.InvalidInput;
  240. }
  241. lock (map)
  242. {
  243. if (map.DmaMapAddress != 0)
  244. {
  245. // FIXME:
  246. // To make unmapping work, we need separate address space per channel.
  247. // Right now NVDEC and VIC share the GPU address space which is not correct at all.
  248. // _host1xContext.MemoryAllocator.Free((ulong)map.DmaMapAddress, (uint)map.Size);
  249. // map.DmaMapAddress = 0;
  250. }
  251. }
  252. }
  253. return NvInternalResult.Success;
  254. }
  255. private NvInternalResult SetNvMapFd(ref int nvMapFd)
  256. {
  257. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  258. return NvInternalResult.Success;
  259. }
  260. private NvInternalResult SetTimeout(ref uint timeout)
  261. {
  262. _timeout = timeout;
  263. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  264. return NvInternalResult.Success;
  265. }
  266. private NvInternalResult SubmitGpfifo(Span<byte> arguments)
  267. {
  268. int headerSize = Unsafe.SizeOf<SubmitGpfifoArguments>();
  269. SubmitGpfifoArguments gpfifoSubmissionHeader = MemoryMarshal.Cast<byte, SubmitGpfifoArguments>(arguments)[0];
  270. Span<ulong> gpfifoEntries = MemoryMarshal.Cast<byte, ulong>(arguments.Slice(headerSize)).Slice(0, gpfifoSubmissionHeader.NumEntries);
  271. return SubmitGpfifo(ref gpfifoSubmissionHeader, gpfifoEntries);
  272. }
  273. private NvInternalResult AllocObjCtx(ref AllocObjCtxArguments arguments)
  274. {
  275. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  276. return NvInternalResult.Success;
  277. }
  278. private NvInternalResult ZcullBind(ref ZcullBindArguments arguments)
  279. {
  280. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  281. return NvInternalResult.Success;
  282. }
  283. private NvInternalResult SetErrorNotifier(ref SetErrorNotifierArguments arguments)
  284. {
  285. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  286. return NvInternalResult.Success;
  287. }
  288. private NvInternalResult SetPriority(ref NvChannelPriority priority)
  289. {
  290. switch (priority)
  291. {
  292. case NvChannelPriority.Low:
  293. _timeslice = 1300; // Timeslice low priority in micro-seconds
  294. break;
  295. case NvChannelPriority.Medium:
  296. _timeslice = 2600; // Timeslice medium priority in micro-seconds
  297. break;
  298. case NvChannelPriority.High:
  299. _timeslice = 5200; // Timeslice high priority in micro-seconds
  300. break;
  301. default:
  302. return NvInternalResult.InvalidInput;
  303. }
  304. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  305. // TODO: disable and preempt channel when GPU scheduler will be implemented.
  306. return NvInternalResult.Success;
  307. }
  308. private NvInternalResult AllocGpfifoEx(ref AllocGpfifoExArguments arguments)
  309. {
  310. _channelSyncpoint.UpdateValue(_device.System.HostSyncpoint);
  311. arguments.Fence = _channelSyncpoint;
  312. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  313. return NvInternalResult.Success;
  314. }
  315. private NvInternalResult AllocGpfifoEx2(ref AllocGpfifoExArguments arguments)
  316. {
  317. _channelSyncpoint.UpdateValue(_device.System.HostSyncpoint);
  318. arguments.Fence = _channelSyncpoint;
  319. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  320. return NvInternalResult.Success;
  321. }
  322. private NvInternalResult SetTimeslice(ref uint timeslice)
  323. {
  324. if (timeslice < 1000 || timeslice > 50000)
  325. {
  326. return NvInternalResult.InvalidInput;
  327. }
  328. _timeslice = timeslice; // in micro-seconds
  329. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  330. // TODO: disable and preempt channel when GPU scheduler will be implemented.
  331. return NvInternalResult.Success;
  332. }
  333. private NvInternalResult SetUserData(ref ulong userData)
  334. {
  335. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  336. return NvInternalResult.Success;
  337. }
  338. protected NvInternalResult SubmitGpfifo(ref SubmitGpfifoArguments header, Span<ulong> entries)
  339. {
  340. if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceWait) && header.Flags.HasFlag(SubmitGpfifoFlags.IncrementWithValue))
  341. {
  342. return NvInternalResult.InvalidInput;
  343. }
  344. if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceWait) && !_device.System.HostSyncpoint.IsSyncpointExpired(header.Fence.Id, header.Fence.Value))
  345. {
  346. Channel.PushHostCommandBuffer(CreateWaitCommandBuffer(header.Fence));
  347. }
  348. Channel.PushEntries(entries);
  349. header.Fence.Id = _channelSyncpoint.Id;
  350. if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement) || header.Flags.HasFlag(SubmitGpfifoFlags.IncrementWithValue))
  351. {
  352. uint incrementCount = header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement) ? 2u : 0u;
  353. if (header.Flags.HasFlag(SubmitGpfifoFlags.IncrementWithValue))
  354. {
  355. incrementCount += header.Fence.Value;
  356. }
  357. header.Fence.Value = _device.System.HostSyncpoint.IncrementSyncpointMaxExt(header.Fence.Id, (int)incrementCount);
  358. }
  359. else
  360. {
  361. header.Fence.Value = _device.System.HostSyncpoint.ReadSyncpointMaxValue(header.Fence.Id);
  362. }
  363. if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement))
  364. {
  365. Channel.PushHostCommandBuffer(CreateIncrementCommandBuffer(ref header.Fence, header.Flags));
  366. }
  367. header.Flags = SubmitGpfifoFlags.None;
  368. _device.Gpu.GPFifo.SignalNewEntries();
  369. return NvInternalResult.Success;
  370. }
  371. public uint GetSyncpointChannel(uint index, bool isClientManaged)
  372. {
  373. if (ChannelSyncpoints[index] != 0)
  374. {
  375. return ChannelSyncpoints[index];
  376. }
  377. ChannelSyncpoints[index] = _device.System.HostSyncpoint.AllocateSyncpoint(isClientManaged);
  378. return ChannelSyncpoints[index];
  379. }
  380. public static uint GetSyncpointDevice(NvHostSyncpt syncpointManager, uint index, bool isClientManaged)
  381. {
  382. if (DeviceSyncpoints[index] != 0)
  383. {
  384. return DeviceSyncpoints[index];
  385. }
  386. DeviceSyncpoints[index] = syncpointManager.AllocateSyncpoint(isClientManaged);
  387. return DeviceSyncpoints[index];
  388. }
  389. private static int[] CreateWaitCommandBuffer(NvFence fence)
  390. {
  391. int[] commandBuffer = new int[4];
  392. // SyncpointValue = fence.Value;
  393. commandBuffer[0] = 0x2001001C;
  394. commandBuffer[1] = (int)fence.Value;
  395. // SyncpointAction(fence.id, increment: false, switch_en: true);
  396. commandBuffer[2] = 0x2001001D;
  397. commandBuffer[3] = (((int)fence.Id << 8) | (0 << 0) | (1 << 4));
  398. return commandBuffer;
  399. }
  400. private int[] CreateIncrementCommandBuffer(ref NvFence fence, SubmitGpfifoFlags flags)
  401. {
  402. bool hasWfi = !flags.HasFlag(SubmitGpfifoFlags.SuppressWfi);
  403. int[] commandBuffer;
  404. int offset = 0;
  405. if (hasWfi)
  406. {
  407. commandBuffer = new int[8];
  408. // WaitForInterrupt(handle)
  409. commandBuffer[offset++] = 0x2001001E;
  410. commandBuffer[offset++] = 0x0;
  411. }
  412. else
  413. {
  414. commandBuffer = new int[6];
  415. }
  416. // SyncpointValue = 0x0;
  417. commandBuffer[offset++] = 0x2001001C;
  418. commandBuffer[offset++] = 0x0;
  419. // Increment the syncpoint 2 times. (mitigate a hardware bug)
  420. // SyncpointAction(fence.id, increment: true, switch_en: false);
  421. commandBuffer[offset++] = 0x2001001D;
  422. commandBuffer[offset++] = (((int)fence.Id << 8) | (1 << 0) | (0 << 4));
  423. // SyncpointAction(fence.id, increment: true, switch_en: false);
  424. commandBuffer[offset++] = 0x2001001D;
  425. commandBuffer[offset++] = (((int)fence.Id << 8) | (1 << 0) | (0 << 4));
  426. return commandBuffer;
  427. }
  428. public override void Close()
  429. {
  430. _host1xContext.Host1x.DestroyContext(_contextId);
  431. Channel.Dispose();
  432. }
  433. private static Host1xContext GetHost1XContext(GpuContext gpu, long pid)
  434. {
  435. return _host1xContextRegistry.GetOrAdd(pid, (long key) => new Host1xContext(gpu, key));
  436. }
  437. public static void Destroy()
  438. {
  439. foreach (Host1xContext host1xContext in _host1xContextRegistry.Values)
  440. {
  441. host1xContext.Dispose();
  442. }
  443. _host1xContextRegistry.Clear();
  444. }
  445. }
  446. }