diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index b391435dce..50c4ec1e38 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs @@ -53,17 +53,25 @@ namespace Ryujinx.HLE.HOS.Services.Nv // TODO: dynamically generate _channelRegistry with attribute } - private int Open(string path) + private int Open(ServiceCtx context, string path) { - if (_fileDeviceRegistry.TryGetValue(path, out Type fileDeviceClass)) + if (context.Process == _owner) { - ConstructorInfo constructor = fileDeviceClass.GetConstructor(new Type[] { typeof(KProcess) }); + if (_fileDeviceRegistry.TryGetValue(path, out Type fileDeviceClass)) + { + ConstructorInfo constructor = fileDeviceClass.GetConstructor(new Type[] { typeof(ServiceCtx) }); - NvFileDevice fileDevice = (NvFileDevice)constructor.Invoke(new object[] { _owner }); + NvFileDevice fileDevice = (NvFileDevice)constructor.Invoke(new object[] { context }); - return _fileDeviceIdRegistry.Add(fileDevice); + return _fileDeviceIdRegistry.Add(fileDevice); + } + else + { + + } } + Logger.PrintWarning(LogClass.ServiceNv, $"Cannot find file device \"{path}\"!"); return -1; @@ -220,7 +228,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv string path = MemoryHelper.ReadAsciiString(context.Memory, pathPtr); - fd = Open(path); + fd = Open(context, path); if (fd == -1) { diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvFileDevice.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvFileDevice.cs index d1300ebc81..4f111c0fa6 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvFileDevice.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvFileDevice.cs @@ -10,9 +10,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices { protected KProcess _owner; - public NvFileDevice(KProcess owner) + public NvFileDevice(ServiceCtx context) { - _owner = owner; + _owner = context.Process; } public KProcess GetOwner() diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuFileDevice.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuFileDevice.cs index a51005e57f..14dc8c88e4 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuFileDevice.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuFileDevice.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu private static ConcurrentDictionary _addressSpaceContextRegistry = new ConcurrentDictionary(); - public NvHostAsGpuFileDevice(KProcess owner) : base(owner) + public NvHostAsGpuFileDevice(ServiceCtx context) : base(context) { } @@ -28,25 +28,25 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu { switch (command.GetNumberValue()) { - case 0x1: + case 0x01: result = CallIoctlMethod(BindChannel, arguments); break; - case 0x2: + case 0x02: result = CallIoctlMethod(AllocSpace, arguments); break; - case 0x3: + case 0x03: result = CallIoctlMethod(FreeSpace, arguments); break; - case 0x5: + case 0x05: result = CallIoctlMethod(UnmapBuffer, arguments); break; - case 0x6: + case 0x06: result = CallIoctlMethod(MapBufferEx, arguments); break; - case 0x8: + case 0x08: result = CallIoctlMethod(GetVaRegions, arguments); break; - case 0x9: + case 0x09: result = CallIoctlMethod(InitializeEx, arguments); break; case 0x14: diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelFileDevice.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelFileDevice.cs index 88289a171b..2ef8ec5e73 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelFileDevice.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelFileDevice.cs @@ -1,40 +1,346 @@ -using Ryujinx.HLE.HOS.Kernel.Process; +using ARMeilleure.Memory; +using Ryujinx.Common.Logging; +using Ryujinx.Graphics; +using Ryujinx.Graphics.Memory; +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu; +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types; +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; using System; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel { class NvHostChannelFileDevice : NvFileDevice { - public NvHostChannelFileDevice(KProcess owner) : base(owner) - { + private uint _timeout; + private uint _submitTimeout; + private uint _timeslice; + private NvGpu _gpu; + private MemoryManager _memory; + public NvHostChannelFileDevice(ServiceCtx context) : base(context) + { + _gpu = context.Device.Gpu; + _memory = context.Memory; + _timeout = 3000; + _submitTimeout = 0; + _timeslice = 0; } public override NvInternalResult Ioctl(NvIoctl command, Span arguments) { NvInternalResult result = NvInternalResult.NotImplemented; - if (command.GetTypeValue() == NvIoctl.NvHostMagic) + if (command.GetTypeValue() == NvIoctl.NvHostCustomMagic) { switch (command.GetNumberValue()) { - + case 0x01: + result = Submit(arguments); + break; + case 0x02: + result = CallIoctlMethod(GetSyncpoint, arguments); + break; + case 0x03: + result = CallIoctlMethod(GetWaitBase, arguments); + break; + case 0x07: + result = CallIoctlMethod(SetSubmitTimeout, arguments); + break; + case 0x09: + result = MapCommandBuffer(arguments); + break; + case 0x0a: + result = UnmapCommandBuffer(arguments); + break; + } + } + else if (command.GetTypeValue() == NvIoctl.NvHostMagic) + { + switch (command.GetNumberValue()) + { + case 0x01: + result = CallIoctlMethod(SetNvMapFd, arguments); + break; + case 0x03: + result = CallIoctlMethod(SetTimeout, arguments); + break; + case 0x08: + result = SubmitGpfifo(arguments); + break; + case 0x09: + result = CallIoctlMethod(AllocObjCtx, arguments); + break; + case 0x0b: + result = CallIoctlMethod(ZcullBind, arguments); + break; + case 0x0c: + result = CallIoctlMethod(SetErrorNotifier, arguments); + break; + case 0x0d: + result = CallIoctlMethod(SetPriority, arguments); + break; + case 0x18: + result = CallIoctlMethod(AllocGpfifoEx, arguments); + break; + case 0x1a: + result = CallIoctlMethod(AllocGpfifoEx2, arguments); + break; + case 0x1d: + result = CallIoctlMethod(SetTimeslice, arguments); + break; } } else if (command.GetTypeValue() == NvIoctl.NvGpuMagic) { switch (command.GetNumberValue()) { - + case 0x14: + result = CallIoctlMethod(SetUserData, arguments); + break; } } return result; } + private NvInternalResult Submit(Span arguments) + { + int headerSize = Marshal.SizeOf(); + SubmitArguments submitHeader = MemoryMarshal.Cast(arguments)[0]; + Span commandBufferEntries = MemoryMarshal.Cast(arguments.Slice(headerSize)).Slice(0, submitHeader.CmdBufsCount); + NvGpuVmm vmm = NvHostAsGpuFileDevice.GetAddressSpaceContext(_owner).Vmm; + + foreach (CommandBuffer commandBufferEntry in commandBufferEntries) + { + NvMapHandle map = NvMapFileDevice.GetMapFromHandle(_owner, commandBufferEntry.MemoryId); + + int[] cmdBufData = new int[commandBufferEntry.WordsCount]; + + for (int offset = 0; offset < cmdBufData.Length; offset++) + { + cmdBufData[offset] = _memory.ReadInt32(map.Address + commandBufferEntry.Offset + offset * 4); + } + + _gpu.PushCommandBuffer(vmm, cmdBufData); + } + + return NvInternalResult.Success; + } + + private NvInternalResult GetSyncpoint(ref GetParameterArguments arguments) + { + arguments.Value = 0; + + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult GetWaitBase(ref GetParameterArguments arguments) + { + arguments.Value = 0; + + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult SetSubmitTimeout(ref uint submitTimeout) + { + _submitTimeout = submitTimeout; + + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult MapCommandBuffer(Span arguments) + { + int headerSize = Marshal.SizeOf(); + MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast(arguments)[0]; + Span commandBufferEntries = MemoryMarshal.Cast(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries); + NvGpuVmm vmm = NvHostAsGpuFileDevice.GetAddressSpaceContext(_owner).Vmm; + + foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries) + { + NvMapHandle map = NvMapFileDevice.GetMapFromHandle(_owner, commandBufferEntry.MapHandle); + + if (map == null) + { + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!"); + + return NvInternalResult.InvalidInput; + } + + lock (map) + { + if (map.DmaMapAddress == 0) + { + map.DmaMapAddress = vmm.MapLow(map.Address, map.Size); + } + + commandBufferEntry.MapAddress = (int)map.DmaMapAddress; + } + } + + return NvInternalResult.Success; + } + + private NvInternalResult UnmapCommandBuffer(Span arguments) + { + int headerSize = Marshal.SizeOf(); + MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast(arguments)[0]; + Span commandBufferEntries = MemoryMarshal.Cast(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries); + NvGpuVmm vmm = NvHostAsGpuFileDevice.GetAddressSpaceContext(_owner).Vmm; + + // TODO + + foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries) + { + NvMapHandle map = NvMapFileDevice.GetMapFromHandle(_owner, commandBufferEntry.MapHandle); + + if (map == null) + { + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!"); + + return NvInternalResult.InvalidInput; + } + + lock (map) + { + if (map.DmaMapAddress != 0) + { + vmm.Free(map.DmaMapAddress, map.Size); + + map.DmaMapAddress = 0; + } + } + } + + return NvInternalResult.Success; + } + + private NvInternalResult SetNvMapFd(ref int nvMapFd) + { + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult SetTimeout(ref uint timeout) + { + _timeout = timeout; + + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult SubmitGpfifo(Span arguments) + { + int headerSize = Marshal.SizeOf(); + SubmitGpfifoArguments gpfifoSubmissionHeader = MemoryMarshal.Cast(arguments)[0]; + Span gpfifoEntries = MemoryMarshal.Cast(arguments.Slice(headerSize)).Slice(0, gpfifoSubmissionHeader.NumEntries); + + NvGpuVmm vmm = NvHostAsGpuFileDevice.GetAddressSpaceContext(_owner).Vmm; + + foreach (long entry in gpfifoEntries) + { + _gpu.Pusher.Push(vmm, entry); + } + + gpfifoSubmissionHeader.Fence.Id = 0; + gpfifoSubmissionHeader.Fence.Value = 0; + + return NvInternalResult.Success; + } + + private NvInternalResult AllocObjCtx(ref AllocObjCtxArguments arguments) + { + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult ZcullBind(ref ZcullBindArguments arguments) + { + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult SetErrorNotifier(ref SetErrorNotifierArguments arguments) + { + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult SetPriority(ref NvChannelPriority priority) + { + switch (priority) + { + case NvChannelPriority.Low: + _timeslice = 1300; // Timeslice low priority in micro-seconds + break; + case NvChannelPriority.Medium: + _timeslice = 2600; // Timeslice medium priority in micro-seconds + break; + case NvChannelPriority.High: + _timeslice = 5200; // Timeslice high priority in micro-seconds + break; + default: + return NvInternalResult.InvalidInput; + } + + Logger.PrintStub(LogClass.ServiceNv); + + // TODO: disable and preempt channel when GPU scheduler will be implemented. + + return NvInternalResult.Success; + } + + private NvInternalResult AllocGpfifoEx(ref AllocGpfifoExArguments arguments) + { + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult AllocGpfifoEx2(ref AllocGpfifoExArguments arguments) + { + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult SetTimeslice(ref uint timeslice) + { + if (timeslice < 1000 || timeslice > 50000) + { + return NvInternalResult.InvalidInput; + } + + _timeslice = timeslice; // in micro-seconds + + Logger.PrintStub(LogClass.ServiceNv); + + // TODO: disable and preempt channel when GPU scheduler will be implemented. + + return NvInternalResult.Success; + } + + private NvInternalResult SetUserData(ref ulong userData) + { + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + public override void Close() { - throw new NotImplementedException(); + // TODO } } } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelIoctl.cs deleted file mode 100644 index b6328e4a7b..0000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelIoctl.cs +++ /dev/null @@ -1,371 +0,0 @@ -using ARMeilleure.Memory; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.Memory; -using Ryujinx.HLE.HOS.Kernel.Process; -using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu; -using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; -using System; -using System.Collections.Concurrent; - -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel -{ - class NvHostChannelIoctl - { - private static ConcurrentDictionary _channels; - - static NvHostChannelIoctl() - { - _channels = new ConcurrentDictionary(); - } - - public static NvInternalResult ProcessIoctl(ServiceCtx context, int cmd) - { - switch (cmd & 0xffff) - { - case 0x0001: return Submit (context); - case 0x0002: return GetSyncpoint (context); - case 0x0003: return GetWaitBase (context); - case 0x0007: return SetSubmitTimeout (context); - case 0x0009: return MapBuffer (context); - case 0x000a: return UnmapBuffer (context); - case 0x4714: return SetUserData (context); - case 0x4801: return SetNvMap (context); - case 0x4803: return SetTimeout (context); - case 0x4808: return SubmitGpfifo (context); - case 0x4809: return AllocObjCtx (context); - case 0x480b: return ZcullBind (context); - case 0x480c: return SetErrorNotifier (context); - case 0x480d: return SetPriority (context); - case 0x481a: return AllocGpfifoEx2 (context); - case 0x481b: return KickoffPbWithAttr(context); - case 0x481d: return SetTimeslice (context); - } - - throw new NotImplementedException(cmd.ToString("x8")); - } - - private static NvInternalResult Submit(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvHostChannelSubmit args = MemoryHelper.Read(context.Memory, inputPosition); - - NvGpuVmm vmm = NvHostAsGpuFileDevice.GetAddressSpaceContext(context.Process).Vmm; - - for (int index = 0; index < args.CmdBufsCount; index++) - { - long cmdBufOffset = inputPosition + 0x10 + index * 0xc; - - NvHostChannelCmdBuf cmdBuf = MemoryHelper.Read(context.Memory, cmdBufOffset); - - NvMapHandle map = NvMapFileDevice.GetMapFromHandle(context.Process, cmdBuf.MemoryId); - - int[] cmdBufData = new int[cmdBuf.WordsCount]; - - for (int offset = 0; offset < cmdBufData.Length; offset++) - { - cmdBufData[offset] = context.Memory.ReadInt32(map.Address + cmdBuf.Offset + offset * 4); - } - - context.Device.Gpu.PushCommandBuffer(vmm, cmdBufData); - } - - // TODO: Relocation, waitchecks, etc. - - return NvInternalResult.Success; - } - - private static NvInternalResult GetSyncpoint(ServiceCtx context) - { - // TODO - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvHostChannelGetParamArg args = MemoryHelper.Read(context.Memory, inputPosition); - - args.Value = 0; - - MemoryHelper.Write(context.Memory, outputPosition, args); - - return NvInternalResult.Success; - } - - private static NvInternalResult GetWaitBase(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvHostChannelGetParamArg args = MemoryHelper.Read(context.Memory, inputPosition); - - args.Value = 0; - - MemoryHelper.Write(context.Memory, outputPosition, args); - - return NvInternalResult.Success; - } - - private static NvInternalResult SetSubmitTimeout(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - - GetChannel(context).SubmitTimeout = context.Memory.ReadInt32(inputPosition); - - // TODO: Handle the timeout in the submit method. - - Logger.PrintStub(LogClass.ServiceNv); - - return NvInternalResult.Success; - } - - private static NvInternalResult MapBuffer(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvHostChannelMapBuffer args = MemoryHelper.Read(context.Memory, inputPosition); - - NvGpuVmm vmm = NvHostAsGpuFileDevice.GetAddressSpaceContext(context.Process).Vmm; - - for (int index = 0; index < args.NumEntries; index++) - { - int handle = context.Memory.ReadInt32(inputPosition + 0xc + index * 8); - - NvMapHandle map = NvMapFileDevice.GetMapFromHandle(context.Process, handle); - - if (map == null) - { - Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{handle:x8}!"); - - return NvInternalResult.InvalidInput; - } - - lock (map) - { - if (map.DmaMapAddress == 0) - { - map.DmaMapAddress = vmm.MapLow(map.Address, map.Size); - } - - context.Memory.WriteInt32(outputPosition + 0xc + 4 + index * 8, (int)map.DmaMapAddress); - } - } - - return NvInternalResult.Success; - } - - private static NvInternalResult UnmapBuffer(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - - NvHostChannelMapBuffer args = MemoryHelper.Read(context.Memory, inputPosition); - - NvGpuVmm vmm = NvHostAsGpuFileDevice.GetAddressSpaceContext(context.Process).Vmm; - - for (int index = 0; index < args.NumEntries; index++) - { - int handle = context.Memory.ReadInt32(inputPosition + 0xc + index * 8); - - NvMapHandle map = NvMapFileDevice.GetMapFromHandle(context.Process, handle); - - if (map == null) - { - Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{handle:x8}!"); - - return NvInternalResult.InvalidInput; - } - - lock (map) - { - if (map.DmaMapAddress != 0) - { - vmm.Free(map.DmaMapAddress, map.Size); - - map.DmaMapAddress = 0; - } - } - } - - return NvInternalResult.Success; - } - - private static NvInternalResult SetUserData(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvInternalResult.Success; - } - - private static NvInternalResult SetNvMap(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvInternalResult.Success; - } - - private static NvInternalResult SetTimeout(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - - GetChannel(context).Timeout = context.Memory.ReadInt32(inputPosition); - - Logger.PrintStub(LogClass.ServiceNv); - - return NvInternalResult.Success; - } - - private static NvInternalResult SubmitGpfifo(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvHostChannelSubmitGpfifo args = MemoryHelper.Read(context.Memory, inputPosition); - - NvGpuVmm vmm = NvHostAsGpuFileDevice.GetAddressSpaceContext(context.Process).Vmm; - - for (int index = 0; index < args.NumEntries; index++) - { - long gpfifo = context.Memory.ReadInt64(inputPosition + 0x18 + index * 8); - - PushGpfifo(context, vmm, gpfifo); - } - - args.SyncptId = 0; - args.SyncptValue = 0; - - MemoryHelper.Write(context.Memory, outputPosition, args); - - return NvInternalResult.Success; - } - - private static NvInternalResult AllocObjCtx(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvInternalResult.Success; - } - - private static NvInternalResult ZcullBind(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvInternalResult.Success; - } - - private static NvInternalResult SetErrorNotifier(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvInternalResult.Success; - } - - private static NvInternalResult SetPriority(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - - switch ((NvChannelPriority)context.Memory.ReadInt32(inputPosition)) - { - case NvChannelPriority.Low: - GetChannel(context).Timeslice = 1300; // Timeslice low priority in micro-seconds - break; - case NvChannelPriority.Medium: - GetChannel(context).Timeslice = 2600; // Timeslice medium priority in micro-seconds - break; - case NvChannelPriority.High: - GetChannel(context).Timeslice = 5200; // Timeslice high priority in micro-seconds - break; - default: - return NvInternalResult.InvalidInput; - } - - Logger.PrintStub(LogClass.ServiceNv); - - // TODO: disable and preempt channel when GPU scheduler will be implemented. - - return NvInternalResult.Success; - } - - private static NvInternalResult AllocGpfifoEx2(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvInternalResult.Success; - } - - private static NvInternalResult KickoffPbWithAttr(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvHostChannelSubmitGpfifo args = MemoryHelper.Read(context.Memory, inputPosition); - - NvGpuVmm vmm = NvHostAsGpuFileDevice.GetAddressSpaceContext(context.Process).Vmm; - - for (int index = 0; index < args.NumEntries; index++) - { - long gpfifo = context.Memory.ReadInt64(args.Address + index * 8); - - PushGpfifo(context, vmm, gpfifo); - } - - args.SyncptId = 0; - args.SyncptValue = 0; - - MemoryHelper.Write(context.Memory, outputPosition, args); - - return NvInternalResult.Success; - } - - private static NvInternalResult SetTimeslice(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - int timeslice = context.Memory.ReadInt32(inputPosition); - - if (timeslice < 1000 || timeslice > 50000) - { - return NvInternalResult.InvalidInput; - } - - GetChannel(context).Timeslice = timeslice; // in micro-seconds - - Logger.PrintStub(LogClass.ServiceNv); - - // TODO: disable and preempt channel when GPU scheduler will be implemented. - - return NvInternalResult.Success; - } - - private static void PushGpfifo(ServiceCtx context, NvGpuVmm vmm, long gpfifo) - { - context.Device.Gpu.Pusher.Push(vmm, gpfifo); - } - - public static NvChannel GetChannel(ServiceCtx context) - { - return _channels.GetOrAdd(context.Process, (key) => new NvChannel()); - } - - public static void UnloadProcess(KProcess process) - { - _channels.TryRemove(process, out _); - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocGpfifoExArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocGpfifoExArguments.cs new file mode 100644 index 0000000000..8e5a15235a --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocGpfifoExArguments.cs @@ -0,0 +1,17 @@ +using Ryujinx.HLE.HOS.Services.Nv.Types; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct AllocGpfifoExArguments + { + public uint NumEntries; + public uint NumJobs; + public uint Flags; + public NvFence Fence; + public uint Reserved1; + public uint Reserved2; + public uint Reserved3; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocObjCtxArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocObjCtxArguments.cs new file mode 100644 index 0000000000..fae91622cd --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocObjCtxArguments.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct AllocObjCtxArguments + { + public uint ClassNumber; + public uint Flags; + public ulong ObjectId; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/GetParameterArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/GetParameterArguments.cs new file mode 100644 index 0000000000..425e665f42 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/GetParameterArguments.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct GetParameterArguments + { + public uint Parameter; + public uint Value; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/MapCommandBufferArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/MapCommandBufferArguments.cs new file mode 100644 index 0000000000..6de8ff1a78 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/MapCommandBufferArguments.cs @@ -0,0 +1,21 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct CommandBufferHandle + { + public int MapHandle; + public int MapAddress; + } + + [StructLayout(LayoutKind.Sequential)] + struct MapCommandBufferArguments + { + public int NumEntries; + public int DataAddress; // Ignored by the driver. + public bool AttachHostChDas; + public byte Padding1; + public short Padding2; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannelPriority.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannelPriority.cs index 148a640b51..4112a9fcc6 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannelPriority.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannelPriority.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel { - enum NvChannelPriority + enum NvChannelPriority : uint { Low = 50, Medium = 100, diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelCmdBuf.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelCmdBuf.cs deleted file mode 100644 index 0308912b88..0000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelCmdBuf.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel -{ - [StructLayout(LayoutKind.Sequential, Size = 8, Pack = 4)] - struct NvHostChannelCmdBuf - { - public int MemoryId; - public int Offset; - public int WordsCount; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelGetParamArg.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelGetParamArg.cs deleted file mode 100644 index 7294648469..0000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelGetParamArg.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel -{ - [StructLayout(LayoutKind.Sequential, Size = 8, Pack = 4)] - struct NvHostChannelGetParamArg - { - public int Param; - public int Value; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelMapBuffer.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelMapBuffer.cs deleted file mode 100644 index f516588e89..0000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelMapBuffer.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel -{ - [StructLayout(LayoutKind.Sequential, Size = 0xc, Pack = 4)] - struct NvHostChannelMapBuffer - { - public int NumEntries; - public int DataAddress; // Ignored by the driver. - public bool AttachHostChDas; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelSubmit.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelSubmit.cs deleted file mode 100644 index ef2f24e789..0000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelSubmit.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel -{ - [StructLayout(LayoutKind.Sequential, Size = 8, Pack = 4)] - struct NvHostChannelSubmit - { - public int CmdBufsCount; - public int RelocsCount; - public int SyncptIncrsCount; - public int WaitchecksCount; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelSubmitGpfifo.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelSubmitGpfifo.cs deleted file mode 100644 index e8cb5f0f51..0000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelSubmitGpfifo.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel -{ - struct NvHostChannelSubmitGpfifo - { - public long Address; - public int NumEntries; - public int Flags; - public int SyncptId; - public int SyncptValue; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SetErrorNotifierArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SetErrorNotifierArguments.cs new file mode 100644 index 0000000000..1aba53ca6f --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SetErrorNotifierArguments.cs @@ -0,0 +1,13 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct SetErrorNotifierArguments + { + public ulong Offset; + public ulong Size; + public uint Mem; + public uint Reserved; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitArguments.cs new file mode 100644 index 0000000000..bb2fd1cc07 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitArguments.cs @@ -0,0 +1,21 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct CommandBuffer + { + public int MemoryId; + public int Offset; + public int WordsCount; + } + + [StructLayout(LayoutKind.Sequential)] + struct SubmitArguments + { + public int CmdBufsCount; + public int RelocsCount; + public int SyncptIncrsCount; + public int WaitchecksCount; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoArguments.cs new file mode 100644 index 0000000000..18cdde06b9 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoArguments.cs @@ -0,0 +1,14 @@ +using Ryujinx.HLE.HOS.Services.Nv.Types; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct SubmitGpfifoArguments + { + public long Address; + public int NumEntries; + public int Flags; + public NvFence Fence; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/ZcullBindArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/ZcullBindArguments.cs new file mode 100644 index 0000000000..19a997f433 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/ZcullBindArguments.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct ZcullBindArguments + { + public ulong GpuVirtualAddress; + public uint Mode; + public uint Reserved; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlFileDevice.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlFileDevice.cs index a8c3929823..f872a64ec1 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlFileDevice.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlFileDevice.cs @@ -1,32 +1,60 @@ using Ryujinx.Common.Logging; -using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl.Types; +using Ryujinx.HLE.HOS.Services.Nv.Types; using Ryujinx.HLE.HOS.Services.Settings; + using System; using System.Text; +using System.Threading; namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl { internal class NvHostCtrlFileDevice : NvFileDevice { - private bool _isProductionMode; + private const int EventsCount = 64; - public NvHostCtrlFileDevice(KProcess owner) : base(owner) + private bool _isProductionMode; + private NvHostSyncpt _syncpt; + private NvHostEvent[] _events; + + public NvHostCtrlFileDevice(ServiceCtx context) : base(context) { if (NxSettings.Settings.TryGetValue("nv!rmos_set_production_mode", out object productionModeSetting)) { _isProductionMode = ((string)productionModeSetting) != "0"; // Default value is "" } + else + { + _isProductionMode = true; + } + + _syncpt = new NvHostSyncpt(); + _events = new NvHostEvent[EventsCount]; } public override NvInternalResult Ioctl(NvIoctl command, Span arguments) { NvInternalResult result = NvInternalResult.NotImplemented; - if (command.GetTypeValue() == NvIoctl.NvHostMagic) + if (command.GetTypeValue() == NvIoctl.NvHostCustomMagic) { switch (command.GetNumberValue()) { + case 0x14: + result = CallIoctlMethod(SyncptRead, arguments); + break; + case 0x15: + result = CallIoctlMethod(SyncptIncr, arguments); + break; + case 0x16: + result = CallIoctlMethod(SyncptWait, arguments); + break; + case 0x19: + result = CallIoctlMethod(SyncptWaitEx, arguments); + break; + case 0x1a: + result = CallIoctlMethod(SyncptReadMax, arguments); + break; case 0x1b: // As Marshal cannot handle unaligned arrays, we do everything by hand here. GetConfigurationArguments configArgument = GetConfigurationArguments.FromSpan(arguments); @@ -37,6 +65,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl configArgument.CopyTo(arguments); } break; + case 0x1d: + result = CallIoctlMethod(EventWait, arguments); + break; + case 0x1e: + result = CallIoctlMethod(EventWaitAsync, arguments); + break; + case 0x1f: + result = CallIoctlMethod(EventRegister, arguments); + break; default: break; } @@ -45,6 +82,38 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl return result; } + private NvInternalResult SyncptRead(ref NvFence arguments) + { + return SyncptReadMinOrMax(ref arguments, max: false); + } + + private NvInternalResult SyncptIncr(ref uint id) + { + if (id >= NvHostSyncpt.SyncptsCount) + { + return NvInternalResult.InvalidInput; + } + + _syncpt.Increment((int)id); + + return NvInternalResult.Success; + } + + private NvInternalResult SyncptWait(ref SyncptWaitArguments arguments) + { + return SyncptWait(ref arguments, out _); + } + + private NvInternalResult SyncptWaitEx(ref SyncptWaitExArguments arguments) + { + return SyncptWait(ref arguments.Input, out arguments.Value); + } + + private NvInternalResult SyncptReadMax(ref NvFence arguments) + { + return SyncptReadMinOrMax(ref arguments, max: true); + } + private NvInternalResult GetConfig(GetConfigurationArguments arguments) { if (!_isProductionMode && NxSettings.Settings.TryGetValue($"{arguments.Domain}!{arguments.Parameter}".ToLower(), out object nvSetting)) @@ -87,6 +156,222 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl return NvInternalResult.InvalidInput; } + private NvInternalResult EventWait(ref EventWaitArguments arguments) + { + return EventWait(ref arguments, async: false); + } + + private NvInternalResult EventWaitAsync(ref EventWaitArguments arguments) + { + return EventWait(ref arguments, async: true); + } + + private NvInternalResult EventRegister(ref uint userEventId) + { + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult SyncptReadMinOrMax(ref NvFence arguments, bool max) + { + if (arguments.Id >= NvHostSyncpt.SyncptsCount) + { + return NvInternalResult.InvalidInput; + } + + if (max) + { + arguments.Value = (uint)_syncpt.GetMax((int)arguments.Id); + } + else + { + arguments.Value = (uint)_syncpt.GetMin((int)arguments.Id); + } + + return NvInternalResult.Success; + } + + private NvInternalResult SyncptWait(ref SyncptWaitArguments arguments, out int value) + { + if (arguments.Id >= NvHostSyncpt.SyncptsCount) + { + value = 0; + + return NvInternalResult.InvalidInput; + } + + NvInternalResult result; + + if (_syncpt.MinCompare((int)arguments.Id, arguments.Thresh)) + { + result = NvInternalResult.Success; + } + else if (arguments.Timeout == 0) + { + result = NvInternalResult.TryAgain; + } + else + { + Logger.PrintDebug(LogClass.ServiceNv, $"Waiting syncpt with timeout of {arguments.Timeout}ms..."); + + using (ManualResetEvent waitEvent = new ManualResetEvent(false)) + { + _syncpt.AddWaiter(arguments.Thresh, waitEvent); + + // Note: Negative (> INT_MAX) timeouts aren't valid on .NET, + // in this case we just use the maximum timeout possible. + int timeout = arguments.Timeout; + + if (timeout < -1) + { + timeout = int.MaxValue; + } + + if (timeout == -1) + { + waitEvent.WaitOne(); + + result = NvInternalResult.Success; + } + else if (waitEvent.WaitOne(timeout)) + { + result = NvInternalResult.Success; + } + else + { + result = NvInternalResult.TimedOut; + } + } + + Logger.PrintDebug(LogClass.ServiceNv, "Resuming..."); + } + + value = _syncpt.GetMin((int)arguments.Id); + + return result; + } + + private NvInternalResult EventWait(ref EventWaitArguments arguments, bool async) + { + if (arguments.Id >= NvHostSyncpt.SyncptsCount) + { + return NvInternalResult.InvalidInput; + } + + if (_syncpt.MinCompare(arguments.Id, arguments.Thresh)) + { + arguments.Value = _syncpt.GetMin(arguments.Id); + + return NvInternalResult.Success; + } + + if (!async) + { + arguments.Value = 0; + } + + if (arguments.Timeout == 0) + { + return NvInternalResult.TryAgain; + } + + NvHostEvent Event; + + NvInternalResult result; + + int eventIndex; + + if (async) + { + eventIndex = arguments.Value; + + if ((uint)eventIndex >= EventsCount) + { + return NvInternalResult.InvalidInput; + } + + Event = _events[eventIndex]; + } + else + { + Event = GetFreeEvent(arguments.Id, out eventIndex); + } + + if (Event != null && + (Event.State == NvHostEventState.Registered || + Event.State == NvHostEventState.Free)) + { + Event.Id = arguments.Id; + Event.Thresh = arguments.Thresh; + + Event.State = NvHostEventState.Waiting; + + if (!async) + { + arguments.Value = ((arguments.Id & 0xfff) << 16) | 0x10000000; + } + else + { + arguments.Value = arguments.Id << 4; + } + + arguments.Value |= eventIndex; + + result = NvInternalResult.TryAgain; + } + else + { + result = NvInternalResult.InvalidInput; + } + + return result; + } + + private NvHostEvent GetFreeEvent(int id, out int eventIndex) + { + eventIndex = EventsCount; + + int nullIndex = EventsCount; + + for (int index = 0; index < EventsCount; index++) + { + NvHostEvent Event = _events[index]; + + if (Event != null) + { + if (Event.State == NvHostEventState.Registered || + Event.State == NvHostEventState.Free) + { + eventIndex = index; + + if (Event.Id == id) + { + return Event; + } + } + } + else if (nullIndex == EventsCount) + { + nullIndex = index; + } + } + + if (nullIndex < EventsCount) + { + eventIndex = nullIndex; + + return _events[nullIndex] = new NvHostEvent(); + } + + if (eventIndex < EventsCount) + { + return _events[eventIndex]; + } + + return null; + } + public override void Close() { // TODO diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlIoctl.cs deleted file mode 100644 index 14261a75e5..0000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlIoctl.cs +++ /dev/null @@ -1,404 +0,0 @@ -using ARMeilleure.Memory; -using Ryujinx.Common.Logging; -using Ryujinx.HLE.HOS.Kernel.Process; -using Ryujinx.HLE.HOS.Services.Settings; -using System; -using System.Collections.Concurrent; -using System.Text; -using System.Threading; - -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl -{ - class NvHostCtrlIoctl - { - private static ConcurrentDictionary _userCtxs; - - private static bool _isProductionMode = true; - - static NvHostCtrlIoctl() - { - _userCtxs = new ConcurrentDictionary(); - - if (NxSettings.Settings.TryGetValue("nv!rmos_set_production_mode", out object productionModeSetting)) - { - _isProductionMode = ((string)productionModeSetting) != "0"; // Default value is "" - } - } - - public static NvInternalResult ProcessIoctl(ServiceCtx context, int cmd) - { - switch (cmd & 0xffff) - { - case 0x0014: return SyncptRead (context); - case 0x0015: return SyncptIncr (context); - case 0x0016: return SyncptWait (context); - case 0x0019: return SyncptWaitEx (context); - case 0x001a: return SyncptReadMax (context); - case 0x001b: return GetConfig (context); - case 0x001d: return EventWait (context); - case 0x001e: return EventWaitAsync(context); - case 0x001f: return EventRegister (context); - } - - throw new NotImplementedException(cmd.ToString("x8")); - } - - private static NvInternalResult SyncptRead(ServiceCtx context) - { - return SyncptReadMinOrMax(context, max: false); - } - - private static NvInternalResult SyncptIncr(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - - int id = context.Memory.ReadInt32(inputPosition); - - if ((uint)id >= NvHostSyncpt.SyncptsCount) - { - return NvInternalResult.InvalidInput; - } - - GetUserCtx(context).Syncpt.Increment(id); - - return NvInternalResult.Success; - } - - private static NvInternalResult SyncptWait(ServiceCtx context) - { - return SyncptWait(context, extended: false); - } - - private static NvInternalResult SyncptWaitEx(ServiceCtx context) - { - return SyncptWait(context, extended: true); - } - - private static NvInternalResult SyncptReadMax(ServiceCtx context) - { - return SyncptReadMinOrMax(context, max: true); - } - - private static NvInternalResult GetConfig(ServiceCtx context) - { - if (!_isProductionMode) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - string domain = MemoryHelper.ReadAsciiString(context.Memory, inputPosition + 0, 0x41); - string name = MemoryHelper.ReadAsciiString(context.Memory, inputPosition + 0x41, 0x41); - - if (NxSettings.Settings.TryGetValue($"{domain}!{name}", out object nvSetting)) - { - byte[] settingBuffer = new byte[0x101]; - - if (nvSetting is string stringValue) - { - if (stringValue.Length > 0x100) - { - Logger.PrintError(LogClass.ServiceNv, $"{domain}!{name} String value size is too big!"); - } - else - { - settingBuffer = Encoding.ASCII.GetBytes(stringValue + "\0"); - } - } - - if (nvSetting is int intValue) - { - settingBuffer = BitConverter.GetBytes(intValue); - } - else if (nvSetting is bool boolValue) - { - settingBuffer[0] = boolValue ? (byte)1 : (byte)0; - } - else - { - throw new NotImplementedException(nvSetting.GetType().Name); - } - - context.Memory.WriteBytes(outputPosition + 0x82, settingBuffer); - - Logger.PrintDebug(LogClass.ServiceNv, $"Got setting {domain}!{name}"); - } - - return NvInternalResult.Success; - } - - // NOTE: This actually return NotAvailableInProduction but this is directly translated as a InvalidInput before returning the ioctl. - //return NvInternalResult.NotAvailableInProduction; - return NvInternalResult.InvalidInput; - } - - private static NvInternalResult EventWait(ServiceCtx context) - { - return EventWait(context, async: false); - } - - private static NvInternalResult EventWaitAsync(ServiceCtx context) - { - return EventWait(context, async: true); - } - - private static NvInternalResult EventRegister(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - int eventId = context.Memory.ReadInt32(inputPosition); - - Logger.PrintStub(LogClass.ServiceNv); - - return NvInternalResult.Success; - } - - private static NvInternalResult SyncptReadMinOrMax(ServiceCtx context, bool max) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvHostCtrlSyncptRead args = MemoryHelper.Read(context.Memory, inputPosition); - - if ((uint)args.Id >= NvHostSyncpt.SyncptsCount) - { - return NvInternalResult.InvalidInput; - } - - if (max) - { - args.Value = GetUserCtx(context).Syncpt.GetMax(args.Id); - } - else - { - args.Value = GetUserCtx(context).Syncpt.GetMin(args.Id); - } - - MemoryHelper.Write(context.Memory, outputPosition, args); - - return NvInternalResult.Success; - } - - private static NvInternalResult SyncptWait(ServiceCtx context, bool extended) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvHostCtrlSyncptWait args = MemoryHelper.Read(context.Memory, inputPosition); - - NvHostSyncpt syncpt = GetUserCtx(context).Syncpt; - - if ((uint)args.Id >= NvHostSyncpt.SyncptsCount) - { - return NvInternalResult.InvalidInput; - } - - NvInternalResult result; - - if (syncpt.MinCompare(args.Id, args.Thresh)) - { - result = NvInternalResult.Success; - } - else if (args.Timeout == 0) - { - result = NvInternalResult.TryAgain; - } - else - { - Logger.PrintDebug(LogClass.ServiceNv, "Waiting syncpt with timeout of " + args.Timeout + "ms..."); - - using (ManualResetEvent waitEvent = new ManualResetEvent(false)) - { - syncpt.AddWaiter(args.Thresh, waitEvent); - - // Note: Negative (> INT_MAX) timeouts aren't valid on .NET, - // in this case we just use the maximum timeout possible. - int timeout = args.Timeout; - - if (timeout < -1) - { - timeout = int.MaxValue; - } - - if (timeout == -1) - { - waitEvent.WaitOne(); - - result = NvInternalResult.Success; - } - else if (waitEvent.WaitOne(timeout)) - { - result = NvInternalResult.Success; - } - else - { - result = NvInternalResult.TimedOut; - } - } - - Logger.PrintDebug(LogClass.ServiceNv, "Resuming..."); - } - - if (extended) - { - context.Memory.WriteInt32(outputPosition + 0xc, syncpt.GetMin(args.Id)); - } - - return result; - } - - private static NvInternalResult EventWait(ServiceCtx context, bool async) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvHostCtrlSyncptWaitEx args = MemoryHelper.Read(context.Memory, inputPosition); - - if ((uint)args.Id >= NvHostSyncpt.SyncptsCount) - { - return NvInternalResult.InvalidInput; - } - - void WriteArgs() - { - MemoryHelper.Write(context.Memory, outputPosition, args); - } - - NvHostSyncpt syncpt = GetUserCtx(context).Syncpt; - - if (syncpt.MinCompare(args.Id, args.Thresh)) - { - args.Value = syncpt.GetMin(args.Id); - - WriteArgs(); - - return NvInternalResult.Success; - } - - if (!async) - { - args.Value = 0; - } - - if (args.Timeout == 0) - { - WriteArgs(); - - return NvInternalResult.TryAgain; - } - - NvHostEvent Event; - - NvInternalResult result; - - int eventIndex; - - if (async) - { - eventIndex = args.Value; - - if ((uint)eventIndex >= NvHostCtrlUserCtx.EventsCount) - { - return NvInternalResult.InvalidInput; - } - - Event = GetUserCtx(context).Events[eventIndex]; - } - else - { - Event = GetFreeEvent(context, syncpt, args.Id, out eventIndex); - } - - if (Event != null && - (Event.State == NvHostEventState.Registered || - Event.State == NvHostEventState.Free)) - { - Event.Id = args.Id; - Event.Thresh = args.Thresh; - - Event.State = NvHostEventState.Waiting; - - if (!async) - { - args.Value = ((args.Id & 0xfff) << 16) | 0x10000000; - } - else - { - args.Value = args.Id << 4; - } - - args.Value |= eventIndex; - - result = NvInternalResult.TryAgain; - } - else - { - result = NvInternalResult.InvalidInput; - } - - WriteArgs(); - - return result; - } - - private static NvHostEvent GetFreeEvent( - ServiceCtx context, - NvHostSyncpt syncpt, - int id, - out int eventIndex) - { - NvHostEvent[] events = GetUserCtx(context).Events; - - eventIndex = NvHostCtrlUserCtx.EventsCount; - - int nullIndex = NvHostCtrlUserCtx.EventsCount; - - for (int index = 0; index < NvHostCtrlUserCtx.EventsCount; index++) - { - NvHostEvent Event = events[index]; - - if (Event != null) - { - if (Event.State == NvHostEventState.Registered || - Event.State == NvHostEventState.Free) - { - eventIndex = index; - - if (Event.Id == id) - { - return Event; - } - } - } - else if (nullIndex == NvHostCtrlUserCtx.EventsCount) - { - nullIndex = index; - } - } - - if (nullIndex < NvHostCtrlUserCtx.EventsCount) - { - eventIndex = nullIndex; - - return events[nullIndex] = new NvHostEvent(); - } - - if (eventIndex < NvHostCtrlUserCtx.EventsCount) - { - return events[eventIndex]; - } - - return null; - } - - public static NvHostCtrlUserCtx GetUserCtx(ServiceCtx context) - { - return _userCtxs.GetOrAdd(context.Process, (key) => new NvHostCtrlUserCtx()); - } - - public static void UnloadProcess(KProcess process) - { - _userCtxs.TryRemove(process, out _); - } - } -} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/EventWaitArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/EventWaitArguments.cs new file mode 100644 index 0000000000..3f97da1f7f --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/EventWaitArguments.cs @@ -0,0 +1,13 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct EventWaitArguments + { + public int Id; + public int Thresh; + public int Timeout; + public int Value; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlSyncPtRead.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlSyncPtRead.cs deleted file mode 100644 index 8cfac5719c..0000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlSyncPtRead.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl -{ - struct NvHostCtrlSyncptRead - { - public int Id; - public int Value; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlSyncPtWait.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlSyncPtWait.cs deleted file mode 100644 index 401884c46f..0000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlSyncPtWait.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl -{ - struct NvHostCtrlSyncptWait - { - public int Id; - public int Thresh; - public int Timeout; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlSyncPtWaitEx.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlSyncPtWaitEx.cs deleted file mode 100644 index 49f573e267..0000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlSyncPtWaitEx.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl -{ - struct NvHostCtrlSyncptWaitEx - { - public int Id; - public int Thresh; - public int Timeout; - public int Value; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlUserCtx.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlUserCtx.cs deleted file mode 100644 index 0b9d85cf92..0000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlUserCtx.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl -{ - class NvHostCtrlUserCtx - { - public const int LocksCount = 16; - public const int EventsCount = 64; - - public NvHostSyncpt Syncpt { get; private set; } - - public NvHostEvent[] Events { get; private set; } - - public NvHostCtrlUserCtx() - { - Syncpt = new NvHostSyncpt(); - - Events = new NvHostEvent[EventsCount]; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitArguments.cs new file mode 100644 index 0000000000..13ea89be05 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitArguments.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct SyncptWaitArguments + { + public uint Id; + public int Thresh; + public int Timeout; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitExArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitExArguments.cs new file mode 100644 index 0000000000..d04748ba30 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitExArguments.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct SyncptWaitExArguments + { + public SyncptWaitArguments Input; + public int Value; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuFileDevice.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuFileDevice.cs index c5659a11ab..c0c7341f84 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuFileDevice.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuFileDevice.cs @@ -11,8 +11,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu private static Stopwatch _pTimer = new Stopwatch(); private static double _ticksToNs = (1.0 / Stopwatch.Frequency) * 1_000_000_000; - public NvHostCtrlGpuFileDevice(KProcess owner) : base(owner) + public NvHostCtrlGpuFileDevice(ServiceCtx context) : base(context) { + } static NvHostCtrlGpuFileDevice() @@ -28,19 +29,19 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu { switch (command.GetNumberValue()) { - case 0x1: + case 0x01: result = CallIoctlMethod(ZcullGetCtxSize, arguments); break; - case 0x2: + case 0x02: result = CallIoctlMethod(ZcullGetInfo, arguments); break; - case 0x3: + case 0x03: result = CallIoctlMethod(ZbcSetTable, arguments); break; - case 0x5: + case 0x05: result = CallIoctlMethod(GetCharacteristics, arguments); break; - case 0x6: + case 0x06: result = CallIoctlMethod(GetTpcMasks, arguments); break; case 0x14: diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapFileDevice.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapFileDevice.cs index b4ecfe1bfb..d59e298622 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapFileDevice.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapFileDevice.cs @@ -13,9 +13,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap private static ConcurrentDictionary _maps = new ConcurrentDictionary(); - public NvMapFileDevice(KProcess owner) : base(owner) + public NvMapFileDevice(ServiceCtx context) : base(context) { - IdDictionary dict = _maps.GetOrAdd(owner, (key) => new IdDictionary()); + IdDictionary dict = _maps.GetOrAdd(_owner, (key) => new IdDictionary()); dict.Add(0, new NvMapHandle()); } @@ -24,36 +24,36 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { NvInternalResult result = NvInternalResult.NotImplemented; - if (command.GetTypeValue() == NvIoctl.NvMapMagic) + if (command.GetTypeValue() == NvIoctl.NvMapCustomMagic) { switch (command.GetNumberValue()) { - case 0x1: + case 0x01: result = CallIoctlMethod(Create, arguments); break; - case 0x3: + case 0x03: result = CallIoctlMethod(FromId, arguments); break; - case 0x4: + case 0x04: result = CallIoctlMethod(Alloc, arguments); break; - case 0x5: + case 0x05: result = CallIoctlMethod(Free, arguments); break; - case 0x9: + case 0x09: result = CallIoctlMethod(Param, arguments); break; - case 0xe: + case 0x0e: result = CallIoctlMethod(GetId, arguments); break; - case 0x2: - case 0x6: - case 0x7: - case 0x8: - case 0xa: - case 0xc: - case 0xd: - case 0xf: + case 0x02: + case 0x06: + case 0x07: + case 0x08: + case 0x0a: + case 0x0c: + case 0x0d: + case 0x0f: case 0x10: case 0x11: result = NvInternalResult.NotSupported; diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs index 88fba9af1c..dd90b2ed04 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs @@ -6,10 +6,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv [StructLayout(LayoutKind.Sequential)] struct NvIoctl { - public const int NvHostMagic = 0x00; - public const int NvMapMagic = 0x01; - public const int NvGpuAsMagic = 0x41; - public const int NvGpuMagic = 0x47; + public const int NvHostCustomMagic = 0x00; + public const int NvMapCustomMagic = 0x01; + public const int NvGpuAsMagic = 0x41; + public const int NvGpuMagic = 0x47; + public const int NvHostMagic = 0x48; private const int NumberBits = 8; private const int TypeBits = 8; diff --git a/Ryujinx.HLE/HOS/Services/Nv/Types/NvFence.cs b/Ryujinx.HLE/HOS/Services/Nv/Types/NvFence.cs new file mode 100644 index 0000000000..1458f482f1 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/Types/NvFence.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x8)] + internal struct NvFence + { + public uint Id; + public uint Value; + } +}