NvHostChannelDeviceFile.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  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<ulong, 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, ulong 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<uint> fenceThresholds = GetSpanAndSkip<uint>(ref arguments, submitHeader.FencesCount);
  133. lock (_device)
  134. {
  135. for (int i = 0; i < syncptIncrs.Length; i++)
  136. {
  137. SyncptIncr syncptIncr = syncptIncrs[i];
  138. uint id = syncptIncr.Id;
  139. fenceThresholds[i] = Context.Device.System.HostSyncpoint.IncrementSyncpointMax(id, syncptIncr.Incrs);
  140. }
  141. foreach (CommandBuffer commandBuffer in commandBuffers)
  142. {
  143. NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBuffer.Mem);
  144. var data = _memory.GetSpan(map.Address + commandBuffer.Offset, commandBuffer.WordsCount * 4);
  145. _host1xContext.Host1x.Submit(MemoryMarshal.Cast<byte, int>(data), _contextId);
  146. }
  147. }
  148. return NvInternalResult.Success;
  149. }
  150. private Span<T> GetSpanAndSkip<T>(ref Span<byte> arguments, int count) where T : unmanaged
  151. {
  152. Span<T> output = MemoryMarshal.Cast<byte, T>(arguments).Slice(0, count);
  153. arguments = arguments.Slice(Unsafe.SizeOf<T>() * count);
  154. return output;
  155. }
  156. private NvInternalResult GetSyncpoint(ref GetParameterArguments arguments)
  157. {
  158. if (arguments.Parameter >= MaxModuleSyncpoint)
  159. {
  160. return NvInternalResult.InvalidInput;
  161. }
  162. if (ChannelResourcePolicy == ResourcePolicy.Device)
  163. {
  164. arguments.Value = GetSyncpointDevice(_device.System.HostSyncpoint, arguments.Parameter, false);
  165. }
  166. else
  167. {
  168. arguments.Value = GetSyncpointChannel(arguments.Parameter, false);
  169. }
  170. if (arguments.Value == 0)
  171. {
  172. return NvInternalResult.TryAgain;
  173. }
  174. return NvInternalResult.Success;
  175. }
  176. private NvInternalResult GetWaitBase(ref GetParameterArguments arguments)
  177. {
  178. arguments.Value = 0;
  179. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  180. return NvInternalResult.Success;
  181. }
  182. private NvInternalResult SetSubmitTimeout(ref uint submitTimeout)
  183. {
  184. _submitTimeout = submitTimeout;
  185. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  186. return NvInternalResult.Success;
  187. }
  188. private NvInternalResult MapCommandBuffer(Span<byte> arguments)
  189. {
  190. int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
  191. MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
  192. Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries);
  193. foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
  194. {
  195. NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle);
  196. if (map == null)
  197. {
  198. Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!");
  199. return NvInternalResult.InvalidInput;
  200. }
  201. lock (map)
  202. {
  203. if (map.DmaMapAddress == 0)
  204. {
  205. ulong va = _host1xContext.MemoryAllocator.GetFreeAddress((ulong)map.Size, out ulong freeAddressStartPosition, 1, MemoryManager.PageSize);
  206. if (va != NvMemoryAllocator.PteUnmapped && va <= uint.MaxValue && (va + (uint)map.Size) <= uint.MaxValue)
  207. {
  208. _host1xContext.MemoryAllocator.AllocateRange(va, (uint)map.Size, freeAddressStartPosition);
  209. _host1xContext.Smmu.Map(map.Address, va, (uint)map.Size, PteKind.Pitch); // FIXME: This should not use the GMMU.
  210. map.DmaMapAddress = va;
  211. }
  212. else
  213. {
  214. map.DmaMapAddress = NvMemoryAllocator.PteUnmapped;
  215. }
  216. }
  217. commandBufferEntry.MapAddress = (int)map.DmaMapAddress;
  218. }
  219. }
  220. return NvInternalResult.Success;
  221. }
  222. private NvInternalResult UnmapCommandBuffer(Span<byte> arguments)
  223. {
  224. int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
  225. MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
  226. Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries);
  227. foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
  228. {
  229. NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle);
  230. if (map == null)
  231. {
  232. Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!");
  233. return NvInternalResult.InvalidInput;
  234. }
  235. lock (map)
  236. {
  237. if (map.DmaMapAddress != 0)
  238. {
  239. // FIXME:
  240. // To make unmapping work, we need separate address space per channel.
  241. // Right now NVDEC and VIC share the GPU address space which is not correct at all.
  242. // _host1xContext.MemoryAllocator.Free((ulong)map.DmaMapAddress, (uint)map.Size);
  243. // map.DmaMapAddress = 0;
  244. }
  245. }
  246. }
  247. return NvInternalResult.Success;
  248. }
  249. private NvInternalResult SetNvMapFd(ref int nvMapFd)
  250. {
  251. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  252. return NvInternalResult.Success;
  253. }
  254. private NvInternalResult SetTimeout(ref uint timeout)
  255. {
  256. _timeout = timeout;
  257. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  258. return NvInternalResult.Success;
  259. }
  260. private NvInternalResult SubmitGpfifo(Span<byte> arguments)
  261. {
  262. int headerSize = Unsafe.SizeOf<SubmitGpfifoArguments>();
  263. SubmitGpfifoArguments gpfifoSubmissionHeader = MemoryMarshal.Cast<byte, SubmitGpfifoArguments>(arguments)[0];
  264. Span<ulong> gpfifoEntries = MemoryMarshal.Cast<byte, ulong>(arguments.Slice(headerSize)).Slice(0, gpfifoSubmissionHeader.NumEntries);
  265. return SubmitGpfifo(ref gpfifoSubmissionHeader, gpfifoEntries);
  266. }
  267. private NvInternalResult AllocObjCtx(ref AllocObjCtxArguments arguments)
  268. {
  269. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  270. return NvInternalResult.Success;
  271. }
  272. private NvInternalResult ZcullBind(ref ZcullBindArguments arguments)
  273. {
  274. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  275. return NvInternalResult.Success;
  276. }
  277. private NvInternalResult SetErrorNotifier(ref SetErrorNotifierArguments arguments)
  278. {
  279. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  280. return NvInternalResult.Success;
  281. }
  282. private NvInternalResult SetPriority(ref NvChannelPriority priority)
  283. {
  284. switch (priority)
  285. {
  286. case NvChannelPriority.Low:
  287. _timeslice = 1300; // Timeslice low priority in micro-seconds
  288. break;
  289. case NvChannelPriority.Medium:
  290. _timeslice = 2600; // Timeslice medium priority in micro-seconds
  291. break;
  292. case NvChannelPriority.High:
  293. _timeslice = 5200; // Timeslice high priority in micro-seconds
  294. break;
  295. default:
  296. return NvInternalResult.InvalidInput;
  297. }
  298. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  299. // TODO: disable and preempt channel when GPU scheduler will be implemented.
  300. return NvInternalResult.Success;
  301. }
  302. private NvInternalResult AllocGpfifoEx(ref AllocGpfifoExArguments arguments)
  303. {
  304. _channelSyncpoint.UpdateValue(_device.System.HostSyncpoint);
  305. arguments.Fence = _channelSyncpoint;
  306. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  307. return NvInternalResult.Success;
  308. }
  309. private NvInternalResult AllocGpfifoEx2(ref AllocGpfifoExArguments arguments)
  310. {
  311. _channelSyncpoint.UpdateValue(_device.System.HostSyncpoint);
  312. arguments.Fence = _channelSyncpoint;
  313. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  314. return NvInternalResult.Success;
  315. }
  316. private NvInternalResult SetTimeslice(ref uint timeslice)
  317. {
  318. if (timeslice < 1000 || timeslice > 50000)
  319. {
  320. return NvInternalResult.InvalidInput;
  321. }
  322. _timeslice = timeslice; // in micro-seconds
  323. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  324. // TODO: disable and preempt channel when GPU scheduler will be implemented.
  325. return NvInternalResult.Success;
  326. }
  327. private NvInternalResult SetUserData(ref ulong userData)
  328. {
  329. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  330. return NvInternalResult.Success;
  331. }
  332. protected NvInternalResult SubmitGpfifo(ref SubmitGpfifoArguments header, Span<ulong> entries)
  333. {
  334. if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceWait) && header.Flags.HasFlag(SubmitGpfifoFlags.IncrementWithValue))
  335. {
  336. return NvInternalResult.InvalidInput;
  337. }
  338. if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceWait) && !_device.System.HostSyncpoint.IsSyncpointExpired(header.Fence.Id, header.Fence.Value))
  339. {
  340. Channel.PushHostCommandBuffer(CreateWaitCommandBuffer(header.Fence));
  341. }
  342. header.Fence.Id = _channelSyncpoint.Id;
  343. if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement) || header.Flags.HasFlag(SubmitGpfifoFlags.IncrementWithValue))
  344. {
  345. uint incrementCount = header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement) ? 2u : 0u;
  346. if (header.Flags.HasFlag(SubmitGpfifoFlags.IncrementWithValue))
  347. {
  348. incrementCount += header.Fence.Value;
  349. }
  350. header.Fence.Value = _device.System.HostSyncpoint.IncrementSyncpointMaxExt(header.Fence.Id, (int)incrementCount);
  351. }
  352. else
  353. {
  354. header.Fence.Value = _device.System.HostSyncpoint.ReadSyncpointMaxValue(header.Fence.Id);
  355. }
  356. Channel.PushEntries(entries);
  357. if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement))
  358. {
  359. Channel.PushHostCommandBuffer(CreateIncrementCommandBuffer(ref header.Fence, header.Flags));
  360. }
  361. header.Flags = SubmitGpfifoFlags.None;
  362. _device.Gpu.GPFifo.SignalNewEntries();
  363. return NvInternalResult.Success;
  364. }
  365. public uint GetSyncpointChannel(uint index, bool isClientManaged)
  366. {
  367. if (ChannelSyncpoints[index] != 0)
  368. {
  369. return ChannelSyncpoints[index];
  370. }
  371. ChannelSyncpoints[index] = _device.System.HostSyncpoint.AllocateSyncpoint(isClientManaged);
  372. return ChannelSyncpoints[index];
  373. }
  374. public static uint GetSyncpointDevice(NvHostSyncpt syncpointManager, uint index, bool isClientManaged)
  375. {
  376. if (DeviceSyncpoints[index] != 0)
  377. {
  378. return DeviceSyncpoints[index];
  379. }
  380. DeviceSyncpoints[index] = syncpointManager.AllocateSyncpoint(isClientManaged);
  381. return DeviceSyncpoints[index];
  382. }
  383. private static int[] CreateWaitCommandBuffer(NvFence fence)
  384. {
  385. int[] commandBuffer = new int[4];
  386. // SyncpointValue = fence.Value;
  387. commandBuffer[0] = 0x2001001C;
  388. commandBuffer[1] = (int)fence.Value;
  389. // SyncpointAction(fence.id, increment: false, switch_en: true);
  390. commandBuffer[2] = 0x2001001D;
  391. commandBuffer[3] = (((int)fence.Id << 8) | (0 << 0) | (1 << 4));
  392. return commandBuffer;
  393. }
  394. private int[] CreateIncrementCommandBuffer(ref NvFence fence, SubmitGpfifoFlags flags)
  395. {
  396. bool hasWfi = !flags.HasFlag(SubmitGpfifoFlags.SuppressWfi);
  397. int[] commandBuffer;
  398. int offset = 0;
  399. if (hasWfi)
  400. {
  401. commandBuffer = new int[8];
  402. // WaitForInterrupt(handle)
  403. commandBuffer[offset++] = 0x2001001E;
  404. commandBuffer[offset++] = 0x0;
  405. }
  406. else
  407. {
  408. commandBuffer = new int[6];
  409. }
  410. // SyncpointValue = 0x0;
  411. commandBuffer[offset++] = 0x2001001C;
  412. commandBuffer[offset++] = 0x0;
  413. // Increment the syncpoint 2 times. (mitigate a hardware bug)
  414. // SyncpointAction(fence.id, increment: true, switch_en: false);
  415. commandBuffer[offset++] = 0x2001001D;
  416. commandBuffer[offset++] = (((int)fence.Id << 8) | (1 << 0) | (0 << 4));
  417. // SyncpointAction(fence.id, increment: true, switch_en: false);
  418. commandBuffer[offset++] = 0x2001001D;
  419. commandBuffer[offset++] = (((int)fence.Id << 8) | (1 << 0) | (0 << 4));
  420. return commandBuffer;
  421. }
  422. public override void Close()
  423. {
  424. _host1xContext.Host1x.DestroyContext(_contextId);
  425. Channel.Dispose();
  426. for (int i = 0; i < MaxModuleSyncpoint; i++)
  427. {
  428. if (ChannelSyncpoints[i] != 0)
  429. {
  430. _device.System.HostSyncpoint.ReleaseSyncpoint(ChannelSyncpoints[i]);
  431. ChannelSyncpoints[i] = 0;
  432. }
  433. }
  434. _device.System.HostSyncpoint.ReleaseSyncpoint(_channelSyncpoint.Id);
  435. _channelSyncpoint.Id = 0;
  436. }
  437. private static Host1xContext GetHost1XContext(GpuContext gpu, ulong pid)
  438. {
  439. return _host1xContextRegistry.GetOrAdd(pid, (ulong key) => new Host1xContext(gpu, key));
  440. }
  441. public static void Destroy()
  442. {
  443. foreach (Host1xContext host1xContext in _host1xContextRegistry.Values)
  444. {
  445. host1xContext.Dispose();
  446. }
  447. _host1xContextRegistry.Clear();
  448. }
  449. }
  450. }