NvHostAsGpuDeviceFile.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Graphics.Gpu.Memory;
  3. using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types;
  4. using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel;
  5. using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
  6. using Ryujinx.Memory;
  7. using System;
  8. using System.Diagnostics;
  9. using System.Runtime.CompilerServices;
  10. namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
  11. {
  12. class NvHostAsGpuDeviceFile : NvDeviceFile
  13. {
  14. private const uint SmallPageSize = 0x1000;
  15. private const uint BigPageSize = 0x10000;
  16. private static readonly uint[] _pageSizes = new uint[] { SmallPageSize, BigPageSize };
  17. private const ulong SmallRegionLimit = 0x400000000UL; // 16 GB
  18. private const ulong DefaultUserSize = 1UL << 37;
  19. private struct VmRegion
  20. {
  21. public ulong Start { get; }
  22. public ulong Limit { get; }
  23. public VmRegion(ulong start, ulong limit)
  24. {
  25. Start = start;
  26. Limit = limit;
  27. }
  28. }
  29. private static readonly VmRegion[] _vmRegions = new VmRegion[]
  30. {
  31. new VmRegion((ulong)BigPageSize << 16, SmallRegionLimit),
  32. new VmRegion(SmallRegionLimit, DefaultUserSize)
  33. };
  34. private readonly AddressSpaceContext _asContext;
  35. private readonly NvMemoryAllocator _memoryAllocator;
  36. public NvHostAsGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner)
  37. {
  38. _asContext = new AddressSpaceContext(context.Device.Gpu.CreateMemoryManager(owner));
  39. _memoryAllocator = new NvMemoryAllocator();
  40. }
  41. public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments)
  42. {
  43. NvInternalResult result = NvInternalResult.NotImplemented;
  44. if (command.Type == NvIoctl.NvGpuAsMagic)
  45. {
  46. switch (command.Number)
  47. {
  48. case 0x01:
  49. result = CallIoctlMethod<BindChannelArguments>(BindChannel, arguments);
  50. break;
  51. case 0x02:
  52. result = CallIoctlMethod<AllocSpaceArguments>(AllocSpace, arguments);
  53. break;
  54. case 0x03:
  55. result = CallIoctlMethod<FreeSpaceArguments>(FreeSpace, arguments);
  56. break;
  57. case 0x05:
  58. result = CallIoctlMethod<UnmapBufferArguments>(UnmapBuffer, arguments);
  59. break;
  60. case 0x06:
  61. result = CallIoctlMethod<MapBufferExArguments>(MapBufferEx, arguments);
  62. break;
  63. case 0x08:
  64. result = CallIoctlMethod<GetVaRegionsArguments>(GetVaRegions, arguments);
  65. break;
  66. case 0x09:
  67. result = CallIoctlMethod<InitializeExArguments>(InitializeEx, arguments);
  68. break;
  69. case 0x14:
  70. result = CallIoctlMethod<RemapArguments>(Remap, arguments);
  71. break;
  72. }
  73. }
  74. return result;
  75. }
  76. public override NvInternalResult Ioctl3(NvIoctl command, Span<byte> arguments, Span<byte> inlineOutBuffer)
  77. {
  78. NvInternalResult result = NvInternalResult.NotImplemented;
  79. if (command.Type == NvIoctl.NvGpuAsMagic)
  80. {
  81. switch (command.Number)
  82. {
  83. case 0x08:
  84. // This is the same as the one in ioctl as inlineOutBuffer is empty.
  85. result = CallIoctlMethod<GetVaRegionsArguments>(GetVaRegions, arguments);
  86. break;
  87. }
  88. }
  89. return result;
  90. }
  91. private NvInternalResult BindChannel(ref BindChannelArguments arguments)
  92. {
  93. var channelDeviceFile = INvDrvServices.DeviceFileIdRegistry.GetData<NvHostChannelDeviceFile>(arguments.Fd);
  94. if (channelDeviceFile == null)
  95. {
  96. // TODO: Return invalid Fd error.
  97. }
  98. channelDeviceFile.Channel.BindMemory(_asContext.Gmm);
  99. return NvInternalResult.Success;
  100. }
  101. private NvInternalResult AllocSpace(ref AllocSpaceArguments arguments)
  102. {
  103. ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize;
  104. NvInternalResult result = NvInternalResult.Success;
  105. lock (_asContext)
  106. {
  107. // Note: When the fixed offset flag is not set,
  108. // the Offset field holds the alignment size instead.
  109. if ((arguments.Flags & AddressSpaceFlags.FixedOffset) != 0)
  110. {
  111. bool regionInUse = _memoryAllocator.IsRegionInUse(arguments.Offset, size, out ulong freeAddressStartPosition);
  112. ulong address;
  113. if (!regionInUse)
  114. {
  115. _memoryAllocator.AllocateRange(arguments.Offset, size, freeAddressStartPosition);
  116. address = freeAddressStartPosition;
  117. }
  118. else
  119. {
  120. address = NvMemoryAllocator.PteUnmapped;
  121. }
  122. arguments.Offset = address;
  123. }
  124. else
  125. {
  126. ulong address = _memoryAllocator.GetFreeAddress(size, out ulong freeAddressStartPosition, arguments.Offset);
  127. if (address != NvMemoryAllocator.PteUnmapped)
  128. {
  129. _memoryAllocator.AllocateRange(address, size, freeAddressStartPosition);
  130. }
  131. arguments.Offset = address;
  132. }
  133. if (arguments.Offset == NvMemoryAllocator.PteUnmapped)
  134. {
  135. arguments.Offset = 0;
  136. Logger.Warning?.Print(LogClass.ServiceNv, $"Failed to allocate size {size:x16}!");
  137. result = NvInternalResult.OutOfMemory;
  138. }
  139. else
  140. {
  141. _asContext.AddReservation(arguments.Offset, size);
  142. }
  143. }
  144. return result;
  145. }
  146. private NvInternalResult FreeSpace(ref FreeSpaceArguments arguments)
  147. {
  148. ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize;
  149. NvInternalResult result = NvInternalResult.Success;
  150. lock (_asContext)
  151. {
  152. if (_asContext.RemoveReservation(arguments.Offset))
  153. {
  154. _memoryAllocator.DeallocateRange(arguments.Offset, size);
  155. _asContext.Gmm.Unmap(arguments.Offset, size);
  156. }
  157. else
  158. {
  159. Logger.Warning?.Print(LogClass.ServiceNv,
  160. $"Failed to free offset 0x{arguments.Offset:x16} size 0x{size:x16}!");
  161. result = NvInternalResult.InvalidInput;
  162. }
  163. }
  164. return result;
  165. }
  166. private NvInternalResult UnmapBuffer(ref UnmapBufferArguments arguments)
  167. {
  168. lock (_asContext)
  169. {
  170. if (_asContext.RemoveMap(arguments.Offset, out ulong size))
  171. {
  172. if (size != 0)
  173. {
  174. _memoryAllocator.DeallocateRange(arguments.Offset, size);
  175. _asContext.Gmm.Unmap(arguments.Offset, size);
  176. }
  177. }
  178. else
  179. {
  180. Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid buffer offset {arguments.Offset:x16}!");
  181. }
  182. }
  183. return NvInternalResult.Success;
  184. }
  185. private NvInternalResult MapBufferEx(ref MapBufferExArguments arguments)
  186. {
  187. const string MapErrorMsg = "Failed to map fixed buffer with offset 0x{0:x16}, size 0x{1:x16} and alignment 0x{2:x16}!";
  188. ulong physicalAddress;
  189. if ((arguments.Flags & AddressSpaceFlags.RemapSubRange) != 0)
  190. {
  191. lock (_asContext)
  192. {
  193. if (_asContext.TryGetMapPhysicalAddress(arguments.Offset, out physicalAddress))
  194. {
  195. ulong virtualAddress = arguments.Offset + arguments.BufferOffset;
  196. physicalAddress += arguments.BufferOffset;
  197. _asContext.Gmm.Map(physicalAddress, virtualAddress, arguments.MappingSize);
  198. return NvInternalResult.Success;
  199. }
  200. else
  201. {
  202. Logger.Warning?.Print(LogClass.ServiceNv, $"Address 0x{arguments.Offset:x16} not mapped!");
  203. return NvInternalResult.InvalidInput;
  204. }
  205. }
  206. }
  207. NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, arguments.NvMapHandle);
  208. if (map == null)
  209. {
  210. Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{arguments.NvMapHandle:x8}!");
  211. return NvInternalResult.InvalidInput;
  212. }
  213. ulong pageSize = (ulong)arguments.PageSize;
  214. if (pageSize == 0)
  215. {
  216. pageSize = (ulong)map.Align;
  217. }
  218. physicalAddress = map.Address + arguments.BufferOffset;
  219. ulong size = arguments.MappingSize;
  220. if (size == 0)
  221. {
  222. size = (uint)map.Size;
  223. }
  224. NvInternalResult result = NvInternalResult.Success;
  225. lock (_asContext)
  226. {
  227. // Note: When the fixed offset flag is not set,
  228. // the Offset field holds the alignment size instead.
  229. bool virtualAddressAllocated = (arguments.Flags & AddressSpaceFlags.FixedOffset) == 0;
  230. if (!virtualAddressAllocated)
  231. {
  232. if (_asContext.ValidateFixedBuffer(arguments.Offset, size, pageSize))
  233. {
  234. _asContext.Gmm.Map(physicalAddress, arguments.Offset, size);
  235. }
  236. else
  237. {
  238. string message = string.Format(MapErrorMsg, arguments.Offset, size, pageSize);
  239. Logger.Warning?.Print(LogClass.ServiceNv, message);
  240. result = NvInternalResult.InvalidInput;
  241. }
  242. }
  243. else
  244. {
  245. ulong va = _memoryAllocator.GetFreeAddress(size, out ulong freeAddressStartPosition, pageSize);
  246. if (va != NvMemoryAllocator.PteUnmapped)
  247. {
  248. _memoryAllocator.AllocateRange(va, size, freeAddressStartPosition);
  249. }
  250. _asContext.Gmm.Map(physicalAddress, va, size);
  251. arguments.Offset = va;
  252. }
  253. if (arguments.Offset == NvMemoryAllocator.PteUnmapped)
  254. {
  255. arguments.Offset = 0;
  256. Logger.Warning?.Print(LogClass.ServiceNv, $"Failed to map size 0x{size:x16}!");
  257. result = NvInternalResult.InvalidInput;
  258. }
  259. else
  260. {
  261. _asContext.AddMap(arguments.Offset, size, physicalAddress, virtualAddressAllocated);
  262. }
  263. }
  264. return result;
  265. }
  266. private NvInternalResult GetVaRegions(ref GetVaRegionsArguments arguments)
  267. {
  268. int vaRegionStructSize = Unsafe.SizeOf<VaRegion>();
  269. Debug.Assert(vaRegionStructSize == 0x18);
  270. Debug.Assert(_pageSizes.Length == 2);
  271. uint writeEntries = (uint)(arguments.BufferSize / vaRegionStructSize);
  272. if (writeEntries > _pageSizes.Length)
  273. {
  274. writeEntries = (uint)_pageSizes.Length;
  275. }
  276. for (uint i = 0; i < writeEntries; i++)
  277. {
  278. ref var region = ref arguments.Regions[(int)i];
  279. var vmRegion = _vmRegions[i];
  280. uint pageSize = _pageSizes[i];
  281. region.PageSize = pageSize;
  282. region.Offset = vmRegion.Start;
  283. region.Pages = (vmRegion.Limit - vmRegion.Start) / pageSize;
  284. region.Padding = 0;
  285. }
  286. arguments.BufferSize = (uint)(_pageSizes.Length * vaRegionStructSize);
  287. return NvInternalResult.Success;
  288. }
  289. private NvInternalResult InitializeEx(ref InitializeExArguments arguments)
  290. {
  291. Logger.Stub?.PrintStub(LogClass.ServiceNv);
  292. return NvInternalResult.Success;
  293. }
  294. private NvInternalResult Remap(Span<RemapArguments> arguments)
  295. {
  296. MemoryManager gmm = _asContext.Gmm;
  297. for (int index = 0; index < arguments.Length; index++)
  298. {
  299. ulong mapOffs = (ulong)arguments[index].MapOffset << 16;
  300. ulong gpuVa = (ulong)arguments[index].GpuOffset << 16;
  301. ulong size = (ulong)arguments[index].Pages << 16;
  302. if (arguments[index].NvMapHandle == 0)
  303. {
  304. gmm.Unmap(gpuVa, size);
  305. }
  306. else
  307. {
  308. NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, arguments[index].NvMapHandle);
  309. if (map == null)
  310. {
  311. Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{arguments[index].NvMapHandle:x8}!");
  312. return NvInternalResult.InvalidInput;
  313. }
  314. gmm.Map(mapOffs + map.Address, gpuVa, size);
  315. }
  316. }
  317. return NvInternalResult.Success;
  318. }
  319. public override void Close() { }
  320. }
  321. }