diff --git a/Ryujinx.HLE/HOS/GlobalStateTable.cs b/Ryujinx.HLE/HOS/GlobalStateTable.cs deleted file mode 100644 index 5e5e5ecfa..000000000 --- a/Ryujinx.HLE/HOS/GlobalStateTable.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Ryujinx.HLE.HOS.Kernel.Process; -using System.Collections.Concurrent; -using System.Collections.Generic; - -namespace Ryujinx.HLE.HOS -{ - class GlobalStateTable - { - private ConcurrentDictionary _dictByProcess; - - public GlobalStateTable() - { - _dictByProcess = new ConcurrentDictionary(); - } - - public bool Add(KProcess process, int id, object data) - { - IdDictionary dict = _dictByProcess.GetOrAdd(process, (key) => new IdDictionary()); - - return dict.Add(id, data); - } - - public int Add(KProcess process, object data) - { - IdDictionary dict = _dictByProcess.GetOrAdd(process, (key) => new IdDictionary()); - - return dict.Add(data); - } - - public object GetData(KProcess process, int id) - { - if (_dictByProcess.TryGetValue(process, out IdDictionary dict)) - { - return dict.GetData(id); - } - - return null; - } - - public T GetData(KProcess process, int id) - { - if (_dictByProcess.TryGetValue(process, out IdDictionary dict)) - { - return dict.GetData(id); - } - - return default(T); - } - - public object Delete(KProcess process, int id) - { - if (_dictByProcess.TryGetValue(process, out IdDictionary dict)) - { - return dict.Delete(id); - } - - return null; - } - - public ICollection DeleteProcess(KProcess process) - { - if (_dictByProcess.TryRemove(process, out IdDictionary dict)) - { - return dict.Clear(); - } - - return null; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Android/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Android/NvFlinger.cs index 5580c3be4..63df78e5e 100644 --- a/Ryujinx.HLE/HOS/Services/Android/NvFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/Android/NvFlinger.cs @@ -2,7 +2,7 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS; +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; using System; using System.Collections.Generic; @@ -290,7 +290,7 @@ namespace Ryujinx.HLE.HOS.Services.Android int bufferOffset = _bufferQueue[slot].Data.Buffer.Surfaces[0].Offset; - NvMapHandle map = NvMapIoctl.GetNvMap(context, nvMapHandle); + NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(context.Process, nvMapHandle); long fbAddr = map.Address + bufferOffset; @@ -312,7 +312,7 @@ namespace Ryujinx.HLE.HOS.Services.Android int right = crop.Right; int bottom = crop.Bottom; - NvGpuVmm vmm = NvGpuASIoctl.GetASCtx(context).Vmm; + NvGpuVmm vmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(context.Process).Vmm; _renderer.QueueAction(() => { diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index da34421b7..9e22d17e7 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs @@ -1,104 +1,301 @@ using ARMeilleure.Memory; +using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Process; -using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS; -using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu; +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices; +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl; +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; +using Ryujinx.HLE.HOS.Services.Nv.Types; using System; using System.Collections.Generic; +using System.Reflection; namespace Ryujinx.HLE.HOS.Services.Nv { [Service("nvdrv")] [Service("nvdrv:a")] + [Service("nvdrv:s")] + [Service("nvdrv:t")] class INvDrvServices : IpcService { - private delegate int IoctlProcessor(ServiceCtx context, int cmd); - - private static Dictionary _ioctlProcessors = - new Dictionary() + private static Dictionary _deviceFileRegistry = + new Dictionary() { - { "/dev/nvhost-as-gpu", ProcessIoctlNvGpuAS }, - { "/dev/nvhost-ctrl", ProcessIoctlNvHostCtrl }, - { "/dev/nvhost-ctrl-gpu", ProcessIoctlNvGpuGpu }, - { "/dev/nvhost-gpu", ProcessIoctlNvHostChannel }, - { "/dev/nvhost-nvdec", ProcessIoctlNvHostChannel }, - { "/dev/nvhost-vic", ProcessIoctlNvHostChannel }, - { "/dev/nvmap", ProcessIoctlNvMap } + { "/dev/nvmap", typeof(NvMapDeviceFile) }, + { "/dev/nvhost-ctrl", typeof(NvHostCtrlDeviceFile) }, + { "/dev/nvhost-ctrl-gpu", typeof(NvHostCtrlGpuDeviceFile) }, + { "/dev/nvhost-as-gpu", typeof(NvHostAsGpuDeviceFile) }, + { "/dev/nvhost-gpu", typeof(NvHostGpuDeviceFile) }, + //{ "/dev/nvhost-msenc", typeof(NvHostChannelDeviceFile) }, + { "/dev/nvhost-nvdec", typeof(NvHostChannelDeviceFile) }, + //{ "/dev/nvhost-nvjpg", typeof(NvHostChannelDeviceFile) }, + { "/dev/nvhost-vic", typeof(NvHostChannelDeviceFile) }, + //{ "/dev/nvhost-display", typeof(NvHostChannelDeviceFile) }, }; - public static GlobalStateTable Fds { get; private set; } + private static IdDictionary _deviceFileIdRegistry = new IdDictionary(); - private KEvent _event; + private KProcess _owner; public INvDrvServices(ServiceCtx context) { - _event = new KEvent(context.Device.System); + _owner = null; } - static INvDrvServices() + private int Open(ServiceCtx context, string path) { - Fds = new GlobalStateTable(); + if (context.Process == _owner) + { + if (_deviceFileRegistry.TryGetValue(path, out Type deviceFileClass)) + { + ConstructorInfo constructor = deviceFileClass.GetConstructor(new Type[] { typeof(ServiceCtx) }); + + NvDeviceFile deviceFile = (NvDeviceFile)constructor.Invoke(new object[] { context }); + + return _deviceFileIdRegistry.Add(deviceFile); + } + else + { + Logger.PrintWarning(LogClass.ServiceNv, $"Cannot find file device \"{path}\"!"); + } + } + + return -1; + } + + private NvResult GetIoctlArgument(ServiceCtx context, NvIoctl ioctlCommand, out Span arguments) + { + (long inputDataPosition, long inputDataSize) = context.Request.GetBufferType0x21(0); + (long outputDataPosition, long outputDataSize) = context.Request.GetBufferType0x22(0); + + NvIoctl.Direction ioctlDirection = ioctlCommand.DirectionValue; + uint ioctlSize = ioctlCommand.Size; + + bool isRead = (ioctlDirection & NvIoctl.Direction.Read) != 0; + bool isWrite = (ioctlDirection & NvIoctl.Direction.Write) != 0; + + if ((isWrite && ioctlSize > outputDataSize) || (isRead && ioctlSize > inputDataSize)) + { + arguments = null; + + Logger.PrintWarning(LogClass.ServiceNv, "Ioctl size inconsistency found!"); + + return NvResult.InvalidSize; + } + + if (isRead && isWrite) + { + if (outputDataPosition < inputDataSize) + { + arguments = null; + + Logger.PrintWarning(LogClass.ServiceNv, "Ioctl size inconsistency found!"); + + return NvResult.InvalidSize; + } + + byte[] outputData = new byte[outputDataSize]; + + context.Memory.ReadBytes(inputDataPosition, outputData, 0, (int)inputDataSize); + + arguments = new Span(outputData); + } + else if (isWrite) + { + byte[] outputData = new byte[outputDataSize]; + + arguments = new Span(outputData); + } + else + { + arguments = new Span(context.Memory.ReadBytes(inputDataPosition, inputDataSize)); + } + + return NvResult.Success; + } + + private NvResult GetDeviceFileFromFd(int fd, out NvDeviceFile deviceFile) + { + deviceFile = null; + + if (fd < 0) + { + return NvResult.InvalidParameter; + } + + deviceFile = _deviceFileIdRegistry.GetData(fd); + + if (deviceFile == null) + { + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid file descriptor {fd}"); + + return NvResult.NotImplemented; + } + + if (deviceFile.Owner.Pid != _owner.Pid) + { + return NvResult.AccessDenied; + } + + return NvResult.Success; + } + + private NvResult EnsureInitialized() + { + if (_owner == null) + { + Logger.PrintWarning(LogClass.ServiceNv, "INvDrvServices is not initialized!"); + + return NvResult.NotInitialized; + } + + return NvResult.Success; + } + + private static NvResult ConvertInternalErrorCode(NvInternalResult errorCode) + { + switch (errorCode) + { + case NvInternalResult.Success: + return NvResult.Success; + case NvInternalResult.Unknown0x72: + return NvResult.AlreadyAllocated; + case NvInternalResult.TimedOut: + case NvInternalResult.TryAgain: + case NvInternalResult.Interrupted: + return NvResult.Timeout; + case NvInternalResult.InvalidAddress: + return NvResult.InvalidAddress; + case NvInternalResult.NotSupported: + case NvInternalResult.Unknown0x18: + return NvResult.NotSupported; + case NvInternalResult.InvalidState: + return NvResult.InvalidState; + case NvInternalResult.ReadOnlyAttribute: + return NvResult.ReadOnlyAttribute; + case NvInternalResult.NoSpaceLeft: + case NvInternalResult.FileTooBig: + return NvResult.InvalidSize; + case NvInternalResult.FileTableOverflow: + case NvInternalResult.BadFileNumber: + return NvResult.FileOperationFailed; + case NvInternalResult.InvalidInput: + return NvResult.InvalidValue; + case NvInternalResult.NotADirectory: + return NvResult.DirectoryOperationFailed; + case NvInternalResult.Busy: + return NvResult.Busy; + case NvInternalResult.BadAddress: + return NvResult.InvalidAddress; + case NvInternalResult.AccessDenied: + case NvInternalResult.OperationNotPermitted: + return NvResult.AccessDenied; + case NvInternalResult.OutOfMemory: + return NvResult.InsufficientMemory; + case NvInternalResult.DeviceNotFound: + return NvResult.ModuleNotPresent; + case NvInternalResult.IoError: + return NvResult.ResourceError; + default: + return NvResult.IoctlFailed; + } } [Command(0)] - // Open(buffer path) -> (u32 fd, u32 error_code) + // Open(buffer path) -> (s32 fd, u32 error_code) public ResultCode Open(ServiceCtx context) { - long namePtr = context.Request.SendBuff[0].Position; + NvResult errorCode = EnsureInitialized(); + int fd = -1; - string name = MemoryHelper.ReadAsciiString(context.Memory, namePtr); + if (errorCode == NvResult.Success) + { + long pathPtr = context.Request.SendBuff[0].Position; - int fd = Fds.Add(context.Process, new NvFd(name)); + string path = MemoryHelper.ReadAsciiString(context.Memory, pathPtr); + + fd = Open(context, path); + + if (fd == -1) + { + errorCode = NvResult.FileOperationFailed; + } + } context.ResponseData.Write(fd); - context.ResponseData.Write(0); + context.ResponseData.Write((uint)errorCode); return ResultCode.Success; } [Command(1)] - // Ioctl(u32 fd, u32 rq_id, buffer) -> (u32 error_code, buffer) - [Command(11)] // 3.0.0+ - // Ioctl2(u32, u32, buffer, buffer) -> (u32, buffer) + // Ioctl(s32 fd, u32 ioctl_cmd, buffer in_args) -> (u32 error_code, buffer out_args) public ResultCode Ioctl(ServiceCtx context) { - int fd = context.RequestData.ReadInt32(); - int cmd = context.RequestData.ReadInt32(); + NvResult errorCode = EnsureInitialized(); - NvFd fdData = Fds.GetData(context.Process, fd); - - int result = 0; - - if (_ioctlProcessors.TryGetValue(fdData.Name, out IoctlProcessor process)) + if (errorCode == NvResult.Success) { - result = process(context, cmd); - } - else if (!ServiceConfiguration.IgnoreMissingServices) - { - throw new NotImplementedException($"{fdData.Name} {cmd:x4}"); + int fd = context.RequestData.ReadInt32(); + NvIoctl ioctlCommand = context.RequestData.ReadStruct(); + + errorCode = GetIoctlArgument(context, ioctlCommand, out Span arguments); + + if (errorCode == NvResult.Success) + { + errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile); + + if (errorCode == NvResult.Success) + { + NvInternalResult internalResult = deviceFile.Ioctl(ioctlCommand, arguments); + + if (internalResult == NvInternalResult.NotImplemented) + { + throw new NvIoctlNotImplementedException(context, deviceFile, ioctlCommand); + } + + errorCode = ConvertInternalErrorCode(internalResult); + + if (errorCode == NvResult.Success && (ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0) + { + context.Memory.WriteBytes(context.Request.GetBufferType0x22(0).Position, arguments.ToArray()); + } + } + } } - // TODO: Verify if the error codes needs to be translated. - context.ResponseData.Write(result); + context.ResponseData.Write((uint)errorCode); return ResultCode.Success; } [Command(2)] - // Close(u32 fd) -> u32 error_code + // Close(s32 fd) -> u32 error_code public ResultCode Close(ServiceCtx context) { - int fd = context.RequestData.ReadInt32(); + NvResult errorCode = EnsureInitialized(); - Fds.Delete(context.Process, fd); + if (errorCode == NvResult.Success) + { + int fd = context.RequestData.ReadInt32(); - context.ResponseData.Write(0); + errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile); + + if (errorCode == NvResult.Success) + { + deviceFile.Close(); + + _deviceFileIdRegistry.Delete(fd); + } + } + + context.ResponseData.Write((uint)errorCode); return ResultCode.Success; } @@ -110,33 +307,90 @@ namespace Ryujinx.HLE.HOS.Services.Nv long transferMemSize = context.RequestData.ReadInt64(); int transferMemHandle = context.Request.HandleDesc.ToCopy[0]; - NvMapIoctl.InitializeNvMap(context); + _owner = context.Process; - context.ResponseData.Write(0); + context.ResponseData.Write((uint)NvResult.Success); return ResultCode.Success; } [Command(4)] - // QueryEvent(u32 fd, u32 event_id) -> (u32, handle) + // QueryEvent(s32 fd, u32 event_id) -> (u32, handle) public ResultCode QueryEvent(ServiceCtx context) { - int fd = context.RequestData.ReadInt32(); - int eventId = context.RequestData.ReadInt32(); + NvResult errorCode = EnsureInitialized(); - // TODO: Use Fd/EventId, different channels have different events. - if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out int handle) != KernelResult.Success) + if (errorCode == NvResult.Success) { - throw new InvalidOperationException("Out of handles!"); + int fd = context.RequestData.ReadInt32(); + uint eventId = context.RequestData.ReadUInt32(); + + errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile); + + if (errorCode == NvResult.Success) + { + NvInternalResult internalResult = deviceFile.QueryEvent(out int eventHandle, eventId); + + if (internalResult == NvInternalResult.NotImplemented) + { + throw new NvQueryEventNotImplementedException(context, deviceFile, eventId); + } + + errorCode = ConvertInternalErrorCode(internalResult); + + if (errorCode == NvResult.Success) + { + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(eventHandle); + } + } } - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); - - context.ResponseData.Write(0); + context.ResponseData.Write((uint)errorCode); return ResultCode.Success; } + [Command(5)] + // MapSharedMemory(s32 fd, u32 argument, handle) -> u32 error_code + public ResultCode MapSharedMemory(ServiceCtx context) + { + NvResult errorCode = EnsureInitialized(); + + if (errorCode == NvResult.Success) + { + int fd = context.RequestData.ReadInt32(); + uint argument = context.RequestData.ReadUInt32(); + int sharedMemoryHandle = context.Request.HandleDesc.ToCopy[0]; + + errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile); + + if (errorCode == NvResult.Success) + { + KSharedMemory sharedMemory = context.Process.HandleTable.GetObject(sharedMemoryHandle); + + errorCode = ConvertInternalErrorCode(deviceFile.MapSharedMemory(sharedMemory, argument)); + } + } + + context.ResponseData.Write((uint)errorCode); + + return ResultCode.Success; + } + + [Command(6)] + // GetStatus() -> (unknown<0x20>, u32 error_code) + public ResultCode GetStatus(ServiceCtx context) + { + throw new ServiceNotImplementedException(context); + } + + [Command(7)] + // ForceSetClientPid(u64) -> u32 error_code + public ResultCode ForceSetClientPid(ServiceCtx context) + { + throw new ServiceNotImplementedException(context); + } + [Command(8)] // SetClientPID(u64, pid) -> u32 error_code public ResultCode SetClientPid(ServiceCtx context) @@ -157,7 +411,105 @@ namespace Ryujinx.HLE.HOS.Services.Nv return ResultCode.Success; } - [Command(13)] + [Command(10)] // 3.0.0+ + // InitializeDevtools(u32, handle) -> u32 error_code; + public ResultCode InitializeDevtools(ServiceCtx context) + { + throw new ServiceNotImplementedException(context); + } + + [Command(11)] // 3.0.0+ + // Ioctl2(s32 fd, u32 ioctl_cmd, buffer in_args, buffer inline_in_buffer) -> (u32 error_code, buffer out_args) + public ResultCode Ioctl2(ServiceCtx context) + { + NvResult errorCode = EnsureInitialized(); + + if (errorCode == NvResult.Success) + { + int fd = context.RequestData.ReadInt32(); + NvIoctl ioctlCommand = context.RequestData.ReadStruct(); + + (long inlineInBufferPosition, long inlineInBufferSize) = context.Request.GetBufferType0x21(1); + + errorCode = GetIoctlArgument(context, ioctlCommand, out Span arguments); + + Span inlineInBuffer = new Span(context.Memory.ReadBytes(inlineInBufferPosition, inlineInBufferSize)); + + if (errorCode == NvResult.Success) + { + errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile); + + if (errorCode == NvResult.Success) + { + NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBuffer); + + if (internalResult == NvInternalResult.NotImplemented) + { + throw new NvIoctlNotImplementedException(context, deviceFile, ioctlCommand); + } + + errorCode = ConvertInternalErrorCode(internalResult); + + if (errorCode == NvResult.Success && (ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0) + { + context.Memory.WriteBytes(context.Request.GetBufferType0x22(0).Position, arguments.ToArray()); + } + } + } + } + + context.ResponseData.Write((uint)errorCode); + + return ResultCode.Success; + } + + [Command(12)] // 3.0.0+ + // Ioctl3(s32 fd, u32 ioctl_cmd, buffer in_args) -> (u32 error_code, buffer out_args, buffer inline_out_buffer) + public ResultCode Ioctl3(ServiceCtx context) + { + NvResult errorCode = EnsureInitialized(); + + if (errorCode == NvResult.Success) + { + int fd = context.RequestData.ReadInt32(); + NvIoctl ioctlCommand = context.RequestData.ReadStruct(); + + (long inlineOutBufferPosition, long inlineOutBufferSize) = context.Request.GetBufferType0x22(1); + + errorCode = GetIoctlArgument(context, ioctlCommand, out Span arguments); + + Span inlineOutBuffer = new Span(context.Memory.ReadBytes(inlineOutBufferPosition, inlineOutBufferSize)); + + if (errorCode == NvResult.Success) + { + errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile); + + if (errorCode == NvResult.Success) + { + NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBuffer); + + if (internalResult == NvInternalResult.NotImplemented) + { + throw new NvIoctlNotImplementedException(context, deviceFile, ioctlCommand); + } + + errorCode = ConvertInternalErrorCode(internalResult); + + if (errorCode == NvResult.Success && (ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0) + { + context.Memory.WriteBytes(context.Request.GetBufferType0x22(0).Position, arguments.ToArray()); + context.Memory.WriteBytes(inlineOutBufferPosition, inlineOutBuffer.ToArray()); + } + } + } + } + + context.ResponseData.Write((uint)errorCode); + + return ResultCode.Success; + } + + [Command(13)] // 3.0.0+ // FinishInitialize(unknown<8>) public ResultCode FinishInitialize(ServiceCtx context) { @@ -165,72 +517,5 @@ namespace Ryujinx.HLE.HOS.Services.Nv return ResultCode.Success; } - - private static int ProcessIoctlNvGpuAS(ServiceCtx context, int cmd) - { - return ProcessIoctl(context, cmd, NvGpuASIoctl.ProcessIoctl); - } - - private static int ProcessIoctlNvHostCtrl(ServiceCtx context, int cmd) - { - return ProcessIoctl(context, cmd, NvHostCtrlIoctl.ProcessIoctl); - } - - private static int ProcessIoctlNvGpuGpu(ServiceCtx context, int cmd) - { - return ProcessIoctl(context, cmd, NvGpuGpuIoctl.ProcessIoctl); - } - - private static int ProcessIoctlNvHostChannel(ServiceCtx context, int cmd) - { - return ProcessIoctl(context, cmd, NvHostChannelIoctl.ProcessIoctl); - } - - private static int ProcessIoctlNvMap(ServiceCtx context, int cmd) - { - return ProcessIoctl(context, cmd, NvMapIoctl.ProcessIoctl); - } - - private static int ProcessIoctl(ServiceCtx context, int cmd, IoctlProcessor processor) - { - if (CmdIn(cmd) && context.Request.GetBufferType0x21().Position == 0) - { - Logger.PrintError(LogClass.ServiceNv, "Input buffer is null!"); - - return NvResult.InvalidInput; - } - - if (CmdOut(cmd) && context.Request.GetBufferType0x22().Position == 0) - { - Logger.PrintError(LogClass.ServiceNv, "Output buffer is null!"); - - return NvResult.InvalidInput; - } - - return processor(context, cmd); - } - - private static bool CmdIn(int cmd) - { - return ((cmd >> 30) & 1) != 0; - } - - private static bool CmdOut(int cmd) - { - return ((cmd >> 31) & 1) != 0; - } - - public static void UnloadProcess(KProcess process) - { - Fds.DeleteProcess(process); - - NvGpuASIoctl.UnloadProcess(process); - - NvHostChannelIoctl.UnloadProcess(process); - - NvHostCtrlIoctl.UnloadProcess(process); - - NvMapIoctl.UnloadProcess(process); - } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs new file mode 100644 index 000000000..73007c911 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs @@ -0,0 +1,80 @@ +using Ryujinx.HLE.HOS.Kernel.Memory; +using Ryujinx.HLE.HOS.Kernel.Process; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices +{ + abstract class NvDeviceFile + { + public readonly KProcess Owner; + + public NvDeviceFile(ServiceCtx context) + { + Owner = context.Process; + } + + public virtual NvInternalResult QueryEvent(out int eventHandle, uint eventId) + { + eventHandle = 0; + + return NvInternalResult.NotImplemented; + } + + public virtual NvInternalResult MapSharedMemory(KSharedMemory sharedMemory, uint argument) + { + return NvInternalResult.NotImplemented; + } + + public virtual NvInternalResult Ioctl(NvIoctl command, Span arguments) + { + return NvInternalResult.NotImplemented; + } + + public virtual NvInternalResult Ioctl2(NvIoctl command, Span arguments, Span inlineInBuffer) + { + return NvInternalResult.NotImplemented; + } + + public virtual NvInternalResult Ioctl3(NvIoctl command, Span arguments, Span inlineOutBuffer) + { + return NvInternalResult.NotImplemented; + } + + protected delegate NvInternalResult IoctlProcessor(ref T arguments); + protected delegate NvInternalResult IoctlProcessorSpan(Span arguments); + protected delegate NvInternalResult IoctlProcessorInline(ref T arguments, ref T1 inlineData); + protected delegate NvInternalResult IoctlProcessorInlineSpan(ref T arguments, Span inlineData); + + protected static NvInternalResult CallIoctlMethod(IoctlProcessor callback, Span arguments) where T : struct + { + Debug.Assert(arguments.Length == Unsafe.SizeOf()); + + return callback(ref MemoryMarshal.Cast(arguments)[0]); + } + + protected static NvInternalResult CallIoctlMethod(IoctlProcessorInline callback, Span arguments, Span inlineBuffer) where T : struct where T1 : struct + { + Debug.Assert(arguments.Length == Unsafe.SizeOf()); + Debug.Assert(inlineBuffer.Length == Unsafe.SizeOf()); + + return callback(ref MemoryMarshal.Cast(arguments)[0], ref MemoryMarshal.Cast(inlineBuffer)[0]); + } + + protected static NvInternalResult CallIoctlMethod(IoctlProcessorSpan callback, Span arguments) where T : struct + { + return callback(MemoryMarshal.Cast(arguments)); + } + + protected static NvInternalResult CallIoctlMethod(IoctlProcessorInlineSpan callback, Span arguments, Span inlineBuffer) where T : struct where T1 : struct + { + Debug.Assert(arguments.Length == Unsafe.SizeOf()); + + return callback(ref MemoryMarshal.Cast(arguments)[0], MemoryMarshal.Cast(inlineBuffer)); + } + + public abstract void Close(); + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/NvGpuASIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/NvGpuASIoctl.cs deleted file mode 100644 index 5c8d1fe0d..000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/NvGpuASIoctl.cs +++ /dev/null @@ -1,330 +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.NvMap; -using System; -using System.Collections.Concurrent; - -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS -{ - class NvGpuASIoctl - { - private const int FlagFixedOffset = 1; - - private const int FlagRemapSubRange = 0x100; - - private static ConcurrentDictionary _asCtxs; - - static NvGpuASIoctl() - { - _asCtxs = new ConcurrentDictionary(); - } - - public static int ProcessIoctl(ServiceCtx context, int cmd) - { - switch (cmd & 0xffff) - { - case 0x4101: return BindChannel (context); - case 0x4102: return AllocSpace (context); - case 0x4103: return FreeSpace (context); - case 0x4105: return UnmapBuffer (context); - case 0x4106: return MapBufferEx (context); - case 0x4108: return GetVaRegions(context); - case 0x4109: return InitializeEx(context); - case 0x4114: return Remap (context, cmd); - } - - throw new NotImplementedException(cmd.ToString("x8")); - } - - private static int BindChannel(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvResult.Success; - } - - private static int AllocSpace(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvGpuASAllocSpace args = MemoryHelper.Read(context.Memory, inputPosition); - - NvGpuASCtx asCtx = GetASCtx(context); - - ulong size = (ulong)args.Pages * - (ulong)args.PageSize; - - int result = NvResult.Success; - - lock (asCtx) - { - // Note: When the fixed offset flag is not set, - // the Offset field holds the alignment size instead. - if ((args.Flags & FlagFixedOffset) != 0) - { - args.Offset = asCtx.Vmm.ReserveFixed(args.Offset, (long)size); - } - else - { - args.Offset = asCtx.Vmm.Reserve((long)size, args.Offset); - } - - if (args.Offset < 0) - { - args.Offset = 0; - - Logger.PrintWarning(LogClass.ServiceNv, $"Failed to allocate size {size:x16}!"); - - result = NvResult.OutOfMemory; - } - else - { - asCtx.AddReservation(args.Offset, (long)size); - } - } - - MemoryHelper.Write(context.Memory, outputPosition, args); - - return result; - } - - private static int FreeSpace(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvGpuASAllocSpace args = MemoryHelper.Read(context.Memory, inputPosition); - - NvGpuASCtx asCtx = GetASCtx(context); - - int result = NvResult.Success; - - lock (asCtx) - { - ulong size = (ulong)args.Pages * - (ulong)args.PageSize; - - if (asCtx.RemoveReservation(args.Offset)) - { - asCtx.Vmm.Free(args.Offset, (long)size); - } - else - { - Logger.PrintWarning(LogClass.ServiceNv, - $"Failed to free offset 0x{args.Offset:x16} size 0x{size:x16}!"); - - result = NvResult.InvalidInput; - } - } - - return result; - } - - private static int UnmapBuffer(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvGpuASUnmapBuffer args = MemoryHelper.Read(context.Memory, inputPosition); - - NvGpuASCtx asCtx = GetASCtx(context); - - lock (asCtx) - { - if (asCtx.RemoveMap(args.Offset, out long size)) - { - if (size != 0) - { - asCtx.Vmm.Free(args.Offset, size); - } - } - else - { - Logger.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {args.Offset:x16}!"); - } - } - - return NvResult.Success; - } - - private static int MapBufferEx(ServiceCtx context) - { - const string mapErrorMsg = "Failed to map fixed buffer with offset 0x{0:x16} and size 0x{1:x16}!"; - - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvGpuASMapBufferEx args = MemoryHelper.Read(context.Memory, inputPosition); - - NvGpuASCtx asCtx = GetASCtx(context); - - NvMapHandle map = NvMapIoctl.GetNvMapWithFb(context, args.NvMapHandle); - - if (map == null) - { - Logger.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{args.NvMapHandle:x8}!"); - - return NvResult.InvalidInput; - } - - long pa; - - if ((args.Flags & FlagRemapSubRange) != 0) - { - lock (asCtx) - { - if (asCtx.TryGetMapPhysicalAddress(args.Offset, out pa)) - { - long va = args.Offset + args.BufferOffset; - - pa += args.BufferOffset; - - if (asCtx.Vmm.Map(pa, va, args.MappingSize) < 0) - { - string msg = string.Format(mapErrorMsg, va, args.MappingSize); - - Logger.PrintWarning(LogClass.ServiceNv, msg); - - return NvResult.InvalidInput; - } - - return NvResult.Success; - } - else - { - Logger.PrintWarning(LogClass.ServiceNv, $"Address 0x{args.Offset:x16} not mapped!"); - - return NvResult.InvalidInput; - } - } - } - - pa = map.Address + args.BufferOffset; - - long size = args.MappingSize; - - if (size == 0) - { - size = (uint)map.Size; - } - - int result = NvResult.Success; - - lock (asCtx) - { - // Note: When the fixed offset flag is not set, - // the Offset field holds the alignment size instead. - bool vaAllocated = (args.Flags & FlagFixedOffset) == 0; - - if (!vaAllocated) - { - if (asCtx.ValidateFixedBuffer(args.Offset, size)) - { - args.Offset = asCtx.Vmm.Map(pa, args.Offset, size); - } - else - { - string msg = string.Format(mapErrorMsg, args.Offset, size); - - Logger.PrintWarning(LogClass.ServiceNv, msg); - - result = NvResult.InvalidInput; - } - } - else - { - args.Offset = asCtx.Vmm.Map(pa, size); - } - - if (args.Offset < 0) - { - args.Offset = 0; - - Logger.PrintWarning(LogClass.ServiceNv, $"Failed to map size 0x{size:x16}!"); - - result = NvResult.InvalidInput; - } - else - { - asCtx.AddMap(args.Offset, size, pa, vaAllocated); - } - } - - MemoryHelper.Write(context.Memory, outputPosition, args); - - return result; - } - - private static int GetVaRegions(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvResult.Success; - } - - private static int InitializeEx(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvResult.Success; - } - - private static int Remap(ServiceCtx context, int cmd) - { - int count = ((cmd >> 16) & 0xff) / 0x14; - - long inputPosition = context.Request.GetBufferType0x21().Position; - - for (int index = 0; index < count; index++, inputPosition += 0x14) - { - NvGpuASRemap args = MemoryHelper.Read(context.Memory, inputPosition); - - NvGpuVmm vmm = GetASCtx(context).Vmm; - - NvMapHandle map = NvMapIoctl.GetNvMapWithFb(context, args.NvMapHandle); - - if (map == null) - { - Logger.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{args.NvMapHandle:x8}!"); - - return NvResult.InvalidInput; - } - - long result = vmm.Map(map.Address, (long)(uint)args.Offset << 16, - (long)(uint)args.Pages << 16); - - if (result < 0) - { - Logger.PrintWarning(LogClass.ServiceNv, - $"Page 0x{args.Offset:x16} size 0x{args.Pages:x16} not allocated!"); - - return NvResult.InvalidInput; - } - } - - return NvResult.Success; - } - - public static NvGpuASCtx GetASCtx(ServiceCtx context) - { - return _asCtxs.GetOrAdd(context.Process, (key) => new NvGpuASCtx(context)); - } - - public static void UnloadProcess(KProcess process) - { - _asCtxs.TryRemove(process, out _); - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASAllocSpace.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASAllocSpace.cs deleted file mode 100644 index f0a0db35e..000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASAllocSpace.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS -{ - struct NvGpuASAllocSpace - { - public int Pages; - public int PageSize; - public int Flags; - public int Padding; - public long Offset; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASMapBufferEx.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASMapBufferEx.cs deleted file mode 100644 index 6ef803774..000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASMapBufferEx.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS -{ - struct NvGpuASMapBufferEx - { - public int Flags; - public int Kind; - public int NvMapHandle; - public int PageSize; - public long BufferOffset; - public long MappingSize; - public long Offset; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASRemap.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASRemap.cs deleted file mode 100644 index 0a6f8003e..000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASRemap.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS -{ - struct NvGpuASRemap - { - public short Flags; - public short Kind; - public int NvMapHandle; - public int Padding; - public int Offset; - public int Pages; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASUnmapBuffer.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASUnmapBuffer.cs deleted file mode 100644 index 63476b2fe..000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASUnmapBuffer.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS -{ - struct NvGpuASUnmapBuffer - { - public long Offset; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/NvGpuGpuIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/NvGpuGpuIoctl.cs deleted file mode 100644 index 12f131539..000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/NvGpuGpuIoctl.cs +++ /dev/null @@ -1,190 +0,0 @@ -using ARMeilleure.Memory; -using Ryujinx.Common.Logging; -using System; -using System.Diagnostics; - -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu -{ - class NvGpuGpuIoctl - { - private static Stopwatch _pTimer; - - private static double _ticksToNs; - - static NvGpuGpuIoctl() - { - _pTimer = new Stopwatch(); - - _pTimer.Start(); - - _ticksToNs = (1.0 / Stopwatch.Frequency) * 1_000_000_000; - } - - public static int ProcessIoctl(ServiceCtx context, int cmd) - { - switch (cmd & 0xffff) - { - case 0x4701: return ZcullGetCtxSize (context); - case 0x4702: return ZcullGetInfo (context); - case 0x4703: return ZbcSetTable (context); - case 0x4705: return GetCharacteristics(context); - case 0x4706: return GetTpcMasks (context); - case 0x4714: return GetActiveSlotMask (context); - case 0x471c: return GetGpuTime (context); - } - - throw new NotImplementedException(cmd.ToString("x8")); - } - - private static int ZcullGetCtxSize(ServiceCtx context) - { - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvGpuGpuZcullGetCtxSize args = new NvGpuGpuZcullGetCtxSize - { - Size = 1 - }; - - MemoryHelper.Write(context.Memory, outputPosition, args); - - Logger.PrintStub(LogClass.ServiceNv); - - return NvResult.Success; - } - - private static int ZcullGetInfo(ServiceCtx context) - { - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvGpuGpuZcullGetInfo args = new NvGpuGpuZcullGetInfo - { - WidthAlignPixels = 0x20, - HeightAlignPixels = 0x20, - PixelSquaresByAliquots = 0x400, - AliquotTotal = 0x800, - RegionByteMultiplier = 0x20, - RegionHeaderSize = 0x20, - SubregionHeaderSize = 0xc0, - SubregionWidthAlignPixels = 0x20, - SubregionHeightAlignPixels = 0x40, - SubregionCount = 0x10 - }; - - MemoryHelper.Write(context.Memory, outputPosition, args); - - Logger.PrintStub(LogClass.ServiceNv); - - return NvResult.Success; - } - - private static int ZbcSetTable(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvResult.Success; - } - - private static int GetCharacteristics(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvGpuGpuGetCharacteristics args = MemoryHelper.Read(context.Memory, inputPosition); - - args.BufferSize = 0xa0; - - args.Arch = 0x120; - args.Impl = 0xb; - args.Rev = 0xa1; - args.NumGpc = 0x1; - args.L2CacheSize = 0x40000; - args.OnBoardVideoMemorySize = 0x0; - args.NumTpcPerGpc = 0x2; - args.BusType = 0x20; - args.BigPageSize = 0x20000; - args.CompressionPageSize = 0x20000; - args.PdeCoverageBitCount = 0x1b; - args.AvailableBigPageSizes = 0x30000; - args.GpcMask = 0x1; - args.SmArchSmVersion = 0x503; - args.SmArchSpaVersion = 0x503; - args.SmArchWarpCount = 0x80; - args.GpuVaBitCount = 0x28; - args.Reserved = 0x0; - args.Flags = 0x55; - args.TwodClass = 0x902d; - args.ThreedClass = 0xb197; - args.ComputeClass = 0xb1c0; - args.GpfifoClass = 0xb06f; - args.InlineToMemoryClass = 0xa140; - args.DmaCopyClass = 0xb0b5; - args.MaxFbpsCount = 0x1; - args.FbpEnMask = 0x0; - args.MaxLtcPerFbp = 0x2; - args.MaxLtsPerLtc = 0x1; - args.MaxTexPerTpc = 0x0; - args.MaxGpcCount = 0x1; - args.RopL2EnMask0 = 0x21d70; - args.RopL2EnMask1 = 0x0; - args.ChipName = 0x6230326d67; - args.GrCompbitStoreBaseHw = 0x0; - - MemoryHelper.Write(context.Memory, outputPosition, args); - - return NvResult.Success; - } - - private static int GetTpcMasks(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvGpuGpuGetTpcMasks args = MemoryHelper.Read(context.Memory, inputPosition); - - if (args.MaskBufferSize != 0) - { - args.TpcMask = 3; - } - - MemoryHelper.Write(context.Memory, outputPosition, args); - - return NvResult.Success; - } - - private static int GetActiveSlotMask(ServiceCtx context) - { - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvGpuGpuGetActiveSlotMask args = new NvGpuGpuGetActiveSlotMask - { - Slot = 0x07, - Mask = 0x01 - }; - - MemoryHelper.Write(context.Memory, outputPosition, args); - - Logger.PrintStub(LogClass.ServiceNv); - - return NvResult.Success; - } - - private static int GetGpuTime(ServiceCtx context) - { - long outputPosition = context.Request.GetBufferType0x22().Position; - - context.Memory.WriteInt64(outputPosition, GetPTimerNanoSeconds()); - - return NvResult.Success; - } - - private static long GetPTimerNanoSeconds() - { - double ticks = _pTimer.ElapsedTicks; - - return (long)(ticks * _ticksToNs) & 0xff_ffff_ffff_ffff; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuGetActiveSlotMask.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuGetActiveSlotMask.cs deleted file mode 100644 index 1b4c53451..000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuGetActiveSlotMask.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu -{ - struct NvGpuGpuGetActiveSlotMask - { - public int Slot; - public int Mask; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuGetTpcMasks.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuGetTpcMasks.cs deleted file mode 100644 index bc0966da1..000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuGetTpcMasks.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu -{ - struct NvGpuGpuGetTpcMasks - { - public int MaskBufferSize; - public int Reserved; - public long MaskBufferAddress; - public int TpcMask; - public int Padding; - } -} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuZcullGetCtxSize.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuZcullGetCtxSize.cs deleted file mode 100644 index 8706d51da..000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuZcullGetCtxSize.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu -{ - struct NvGpuGpuZcullGetCtxSize - { - public int Size; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs new file mode 100644 index 000000000..70783b438 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs @@ -0,0 +1,318 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.Memory; +using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types; +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; +using System; +using System.Collections.Concurrent; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu +{ + class NvHostAsGpuDeviceFile : NvDeviceFile + { + private static ConcurrentDictionary _addressSpaceContextRegistry = new ConcurrentDictionary(); + + public NvHostAsGpuDeviceFile(ServiceCtx context) : base(context) { } + + public override NvInternalResult Ioctl(NvIoctl command, Span arguments) + { + NvInternalResult result = NvInternalResult.NotImplemented; + + if (command.Type == NvIoctl.NvGpuAsMagic) + { + switch (command.Number) + { + case 0x01: + result = CallIoctlMethod(BindChannel, arguments); + break; + case 0x02: + result = CallIoctlMethod(AllocSpace, arguments); + break; + case 0x03: + result = CallIoctlMethod(FreeSpace, arguments); + break; + case 0x05: + result = CallIoctlMethod(UnmapBuffer, arguments); + break; + case 0x06: + result = CallIoctlMethod(MapBufferEx, arguments); + break; + case 0x08: + result = CallIoctlMethod(GetVaRegions, arguments); + break; + case 0x09: + result = CallIoctlMethod(InitializeEx, arguments); + break; + case 0x14: + result = CallIoctlMethod(Remap, arguments); + break; + } + } + + return result; + } + + public override NvInternalResult Ioctl3(NvIoctl command, Span arguments, Span inlineOutBuffer) + { + NvInternalResult result = NvInternalResult.NotImplemented; + + if (command.Type == NvIoctl.NvGpuAsMagic) + { + switch (command.Number) + { + case 0x08: + // This is the same as the one in ioctl as inlineOutBuffer is empty. + result = CallIoctlMethod(GetVaRegions, arguments); + break; + } + } + + return result; + } + + private NvInternalResult BindChannel(ref BindChannelArguments arguments) + { + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult AllocSpace(ref AllocSpaceArguments arguments) + { + AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Owner); + + ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize; + + NvInternalResult result = NvInternalResult.Success; + + lock (addressSpaceContext) + { + // Note: When the fixed offset flag is not set, + // the Offset field holds the alignment size instead. + if ((arguments.Flags & AddressSpaceFlags.FixedOffset) != 0) + { + arguments.Offset = addressSpaceContext.Vmm.ReserveFixed(arguments.Offset, (long)size); + } + else + { + arguments.Offset = addressSpaceContext.Vmm.Reserve((long)size, arguments.Offset); + } + + if (arguments.Offset < 0) + { + arguments.Offset = 0; + + Logger.PrintWarning(LogClass.ServiceNv, $"Failed to allocate size {size:x16}!"); + + result = NvInternalResult.OutOfMemory; + } + else + { + addressSpaceContext.AddReservation(arguments.Offset, (long)size); + } + } + + return result; + } + + private NvInternalResult FreeSpace(ref FreeSpaceArguments arguments) + { + AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Owner); + + NvInternalResult result = NvInternalResult.Success; + + lock (addressSpaceContext) + { + ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize; + + if (addressSpaceContext.RemoveReservation(arguments.Offset)) + { + addressSpaceContext.Vmm.Free(arguments.Offset, (long)size); + } + else + { + Logger.PrintWarning(LogClass.ServiceNv, + $"Failed to free offset 0x{arguments.Offset:x16} size 0x{size:x16}!"); + + result = NvInternalResult.InvalidInput; + } + } + + return result; + } + + private NvInternalResult UnmapBuffer(ref UnmapBufferArguments arguments) + { + AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Owner); + + lock (addressSpaceContext) + { + if (addressSpaceContext.RemoveMap(arguments.Offset, out long size)) + { + if (size != 0) + { + addressSpaceContext.Vmm.Free(arguments.Offset, size); + } + } + else + { + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {arguments.Offset:x16}!"); + } + } + + return NvInternalResult.Success; + } + + private NvInternalResult MapBufferEx(ref MapBufferExArguments arguments) + { + const string mapErrorMsg = "Failed to map fixed buffer with offset 0x{0:x16} and size 0x{1:x16}!"; + + AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Owner); + + NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, arguments.NvMapHandle, true); + + if (map == null) + { + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{arguments.NvMapHandle:x8}!"); + + return NvInternalResult.InvalidInput; + } + + long physicalAddress; + + if ((arguments.Flags & AddressSpaceFlags.RemapSubRange) != 0) + { + lock (addressSpaceContext) + { + if (addressSpaceContext.TryGetMapPhysicalAddress(arguments.Offset, out physicalAddress)) + { + long virtualAddress = arguments.Offset + arguments.BufferOffset; + + physicalAddress += arguments.BufferOffset; + + if (addressSpaceContext.Vmm.Map(physicalAddress, virtualAddress, arguments.MappingSize) < 0) + { + string message = string.Format(mapErrorMsg, virtualAddress, arguments.MappingSize); + + Logger.PrintWarning(LogClass.ServiceNv, message); + + return NvInternalResult.InvalidInput; + } + + return NvInternalResult.Success; + } + else + { + Logger.PrintWarning(LogClass.ServiceNv, $"Address 0x{arguments.Offset:x16} not mapped!"); + + return NvInternalResult.InvalidInput; + } + } + } + + physicalAddress = map.Address + arguments.BufferOffset; + + long size = arguments.MappingSize; + + if (size == 0) + { + size = (uint)map.Size; + } + + NvInternalResult result = NvInternalResult.Success; + + lock (addressSpaceContext) + { + // Note: When the fixed offset flag is not set, + // the Offset field holds the alignment size instead. + bool virtualAddressAllocated = (arguments.Flags & AddressSpaceFlags.FixedOffset) == 0; + + if (!virtualAddressAllocated) + { + if (addressSpaceContext.ValidateFixedBuffer(arguments.Offset, size)) + { + arguments.Offset = addressSpaceContext.Vmm.Map(physicalAddress, arguments.Offset, size); + } + else + { + string message = string.Format(mapErrorMsg, arguments.Offset, size); + + Logger.PrintWarning(LogClass.ServiceNv, message); + + result = NvInternalResult.InvalidInput; + } + } + else + { + arguments.Offset = addressSpaceContext.Vmm.Map(physicalAddress, size); + } + + if (arguments.Offset < 0) + { + arguments.Offset = 0; + + Logger.PrintWarning(LogClass.ServiceNv, $"Failed to map size 0x{size:x16}!"); + + result = NvInternalResult.InvalidInput; + } + else + { + addressSpaceContext.AddMap(arguments.Offset, size, physicalAddress, virtualAddressAllocated); + } + } + + return result; + } + + private NvInternalResult GetVaRegions(ref GetVaRegionsArguments arguments) + { + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult InitializeEx(ref InitializeExArguments arguments) + { + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult Remap(Span arguments) + { + for (int index = 0; index < arguments.Length; index++) + { + NvGpuVmm vmm = GetAddressSpaceContext(Owner).Vmm; + + NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, arguments[index].NvMapHandle, true); + + if (map == null) + { + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{arguments[index].NvMapHandle:x8}!"); + + return NvInternalResult.InvalidInput; + } + + long result = vmm.Map(map.Address, (long)arguments[index].Offset << 16, + (long)arguments[index].Pages << 16); + + if (result < 0) + { + Logger.PrintWarning(LogClass.ServiceNv, + $"Page 0x{arguments[index].Offset:x16} size 0x{arguments[index].Pages:x16} not allocated!"); + + return NvInternalResult.InvalidInput; + } + } + + return NvInternalResult.Success; + } + + public override void Close() { } + + public static AddressSpaceContext GetAddressSpaceContext(KProcess process) + { + return _addressSpaceContextRegistry.GetOrAdd(process, (key) => new AddressSpaceContext(process)); + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASCtx.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs similarity index 91% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASCtx.cs rename to Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs index 315fe353e..5c2830000 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASCtx.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs @@ -1,16 +1,20 @@ +using ARMeilleure.Memory; using Ryujinx.Graphics.Memory; +using Ryujinx.HLE.HOS.Kernel.Process; +using System; using System.Collections.Generic; +using System.Text; -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types { - class NvGpuASCtx + class AddressSpaceContext { public NvGpuVmm Vmm { get; private set; } private class Range { - public ulong Start { get; private set; } - public ulong End { get; private set; } + public ulong Start { get; private set; } + public ulong End { get; private set; } public Range(long position, long size) { @@ -22,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS private class MappedMemory : Range { public long PhysicalAddress { get; private set; } - public bool VaAllocated { get; private set; } + public bool VaAllocated { get; private set; } public MappedMemory( long position, @@ -38,9 +42,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS private SortedList _maps; private SortedList _reservations; - public NvGpuASCtx(ServiceCtx context) + public AddressSpaceContext(KProcess process) { - Vmm = new NvGpuVmm(context.Memory); + Vmm = new NvGpuVmm(process.CpuMemory); _maps = new SortedList(); _reservations = new SortedList(); @@ -197,4 +201,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS return ltRg; } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceFlags.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceFlags.cs new file mode 100644 index 000000000..611cf78bf --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceFlags.cs @@ -0,0 +1,11 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types +{ + [Flags] + enum AddressSpaceFlags : uint + { + FixedOffset = 1, + RemapSubRange = 0x100, + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AllocSpaceArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AllocSpaceArguments.cs new file mode 100644 index 000000000..73f746e2d --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AllocSpaceArguments.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct AllocSpaceArguments + { + public uint Pages; + public uint PageSize; + public AddressSpaceFlags Flags; + public uint Padding; + public long Offset; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/BindChannelArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/BindChannelArguments.cs new file mode 100644 index 000000000..9c6568a3f --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/BindChannelArguments.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct BindChannelArguments + { + public int Fd; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/FreeSpaceArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/FreeSpaceArguments.cs new file mode 100644 index 000000000..a853974b4 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/FreeSpaceArguments.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct FreeSpaceArguments + { + public long Offset; + public uint Pages; + public uint PageSize; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/GetVaRegionsArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/GetVaRegionsArguments.cs new file mode 100644 index 000000000..b3a9cf26c --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/GetVaRegionsArguments.cs @@ -0,0 +1,23 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct VaRegion + { + public ulong Offset; + public uint PageSize; + public uint Padding; + public ulong Pages; + } + + [StructLayout(LayoutKind.Sequential)] + struct GetVaRegionsArguments + { + public ulong Unused; + public uint BufferSize; + public uint Padding; + public VaRegion Region0; + public VaRegion Region1; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/InitializeExArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/InitializeExArguments.cs new file mode 100644 index 000000000..882bda591 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/InitializeExArguments.cs @@ -0,0 +1,16 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct InitializeExArguments + { + public uint Flags; + public int AsFd; + public uint BigPageSize; + public uint Reserved; + public ulong Unknown0; + public ulong Unknown1; + public ulong Unknown2; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/MapBufferExArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/MapBufferExArguments.cs new file mode 100644 index 000000000..02e058df6 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/MapBufferExArguments.cs @@ -0,0 +1,16 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct MapBufferExArguments + { + public AddressSpaceFlags Flags; + public int Kind; + public int NvMapHandle; + public int PageSize; + public long BufferOffset; + public long MappingSize; + public long Offset; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/RemapArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/RemapArguments.cs new file mode 100644 index 000000000..0cf324b45 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/RemapArguments.cs @@ -0,0 +1,15 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct RemapArguments + { + public ushort Flags; + public ushort Kind; + public int NvMapHandle; + public int Padding; + public uint Offset; + public uint Pages; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/UnmapBufferArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/UnmapBufferArguments.cs new file mode 100644 index 000000000..1ef880afe --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/UnmapBufferArguments.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types +{ + struct UnmapBufferArguments + { + public long Offset; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs new file mode 100644 index 000000000..80a7ce800 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs @@ -0,0 +1,347 @@ +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.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel +{ + class NvHostChannelDeviceFile : NvDeviceFile + { + private uint _timeout; + private uint _submitTimeout; + private uint _timeslice; + private NvGpu _gpu; + private MemoryManager _memory; + + public NvHostChannelDeviceFile(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.Type == NvIoctl.NvHostCustomMagic) + { + switch (command.Number) + { + 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.Type == NvIoctl.NvHostMagic) + { + switch (command.Number) + { + 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.Type == NvIoctl.NvGpuMagic) + { + switch (command.Number) + { + case 0x14: + result = CallIoctlMethod(SetUserData, arguments); + break; + } + } + + return result; + } + + private NvInternalResult Submit(Span arguments) + { + int headerSize = Unsafe.SizeOf(); + SubmitArguments submitHeader = MemoryMarshal.Cast(arguments)[0]; + Span commandBufferEntries = MemoryMarshal.Cast(arguments.Slice(headerSize)).Slice(0, submitHeader.CmdBufsCount); + NvGpuVmm vmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Owner).Vmm; + + foreach (CommandBuffer commandBufferEntry in commandBufferEntries) + { + NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MemoryId); + + int[] commandBufferData = new int[commandBufferEntry.WordsCount]; + + for (int offset = 0; offset < commandBufferData.Length; offset++) + { + commandBufferData[offset] = _memory.ReadInt32(map.Address + commandBufferEntry.Offset + offset * 4); + } + + _gpu.PushCommandBuffer(vmm, commandBufferData); + } + + 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 = Unsafe.SizeOf(); + MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast(arguments)[0]; + Span commandBufferEntries = MemoryMarshal.Cast(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries); + NvGpuVmm vmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Owner).Vmm; + + foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries) + { + NvMapHandle map = NvMapDeviceFile.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 = Unsafe.SizeOf(); + MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast(arguments)[0]; + Span commandBufferEntries = MemoryMarshal.Cast(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries); + NvGpuVmm vmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Owner).Vmm; + + foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries) + { + NvMapHandle map = NvMapDeviceFile.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 = Unsafe.SizeOf(); + SubmitGpfifoArguments gpfifoSubmissionHeader = MemoryMarshal.Cast(arguments)[0]; + Span gpfifoEntries = MemoryMarshal.Cast(arguments.Slice(headerSize)).Slice(0, gpfifoSubmissionHeader.NumEntries); + + return SubmitGpfifo(ref gpfifoSubmissionHeader, gpfifoEntries); + } + + 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; + } + + protected NvInternalResult SubmitGpfifo(ref SubmitGpfifoArguments header, Span entries) + { + NvGpuVmm vmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Owner).Vmm; + + foreach (long entry in entries) + { + _gpu.Pusher.Push(vmm, entry); + } + + header.Fence.Id = 0; + header.Fence.Value = 0; + + return NvInternalResult.Success; + } + + public override void Close() { } + } +} 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 0d06e7e41..000000000 --- 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.NvGpuAS; -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 int 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 int Submit(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvHostChannelSubmit args = MemoryHelper.Read(context.Memory, inputPosition); - - NvGpuVmm vmm = NvGpuASIoctl.GetASCtx(context).Vmm; - - for (int index = 0; index < args.CmdBufsCount; index++) - { - long cmdBufOffset = inputPosition + 0x10 + index * 0xc; - - NvHostChannelCmdBuf cmdBuf = MemoryHelper.Read(context.Memory, cmdBufOffset); - - NvMapHandle map = NvMapIoctl.GetNvMap(context, 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 NvResult.Success; - } - - private static int 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 NvResult.Success; - } - - private static int 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 NvResult.Success; - } - - private static int 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 NvResult.Success; - } - - private static int MapBuffer(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvHostChannelMapBuffer args = MemoryHelper.Read(context.Memory, inputPosition); - - NvGpuVmm vmm = NvGpuASIoctl.GetASCtx(context).Vmm; - - for (int index = 0; index < args.NumEntries; index++) - { - int handle = context.Memory.ReadInt32(inputPosition + 0xc + index * 8); - - NvMapHandle map = NvMapIoctl.GetNvMap(context, handle); - - if (map == null) - { - Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{handle:x8}!"); - - return NvResult.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 NvResult.Success; - } - - private static int UnmapBuffer(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - - NvHostChannelMapBuffer args = MemoryHelper.Read(context.Memory, inputPosition); - - NvGpuVmm vmm = NvGpuASIoctl.GetASCtx(context).Vmm; - - for (int index = 0; index < args.NumEntries; index++) - { - int handle = context.Memory.ReadInt32(inputPosition + 0xc + index * 8); - - NvMapHandle map = NvMapIoctl.GetNvMap(context, handle); - - if (map == null) - { - Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{handle:x8}!"); - - return NvResult.InvalidInput; - } - - lock (map) - { - if (map.DmaMapAddress != 0) - { - vmm.Free(map.DmaMapAddress, map.Size); - - map.DmaMapAddress = 0; - } - } - } - - return NvResult.Success; - } - - private static int SetUserData(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvResult.Success; - } - - private static int SetNvMap(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvResult.Success; - } - - private static int SetTimeout(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - - GetChannel(context).Timeout = context.Memory.ReadInt32(inputPosition); - - Logger.PrintStub(LogClass.ServiceNv); - - return NvResult.Success; - } - - private static int SubmitGpfifo(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvHostChannelSubmitGpfifo args = MemoryHelper.Read(context.Memory, inputPosition); - - NvGpuVmm vmm = NvGpuASIoctl.GetASCtx(context).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 NvResult.Success; - } - - private static int AllocObjCtx(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvResult.Success; - } - - private static int ZcullBind(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvResult.Success; - } - - private static int SetErrorNotifier(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvResult.Success; - } - - private static int 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 NvResult.InvalidInput; - } - - Logger.PrintStub(LogClass.ServiceNv); - - // TODO: disable and preempt channel when GPU scheduler will be implemented. - - return NvResult.Success; - } - - private static int AllocGpfifoEx2(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - Logger.PrintStub(LogClass.ServiceNv); - - return NvResult.Success; - } - - private static int KickoffPbWithAttr(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvHostChannelSubmitGpfifo args = MemoryHelper.Read(context.Memory, inputPosition); - - NvGpuVmm vmm = NvGpuASIoctl.GetASCtx(context).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 NvResult.Success; - } - - private static int SetTimeslice(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - int timeslice = context.Memory.ReadInt32(inputPosition); - - if (timeslice < 1000 || timeslice > 50000) - { - return NvResult.InvalidInput; - } - - GetChannel(context).Timeslice = timeslice; // in micro-seconds - - Logger.PrintStub(LogClass.ServiceNv); - - // TODO: disable and preempt channel when GPU scheduler will be implemented. - - return NvResult.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/NvHostGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs new file mode 100644 index 000000000..582ba50e9 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs @@ -0,0 +1,78 @@ +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types; +using System; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel +{ + internal class NvHostGpuDeviceFile : NvHostChannelDeviceFile + { + private KEvent _smExceptionBptIntReportEvent; + private KEvent _smExceptionBptPauseReportEvent; + private KEvent _errorNotifierEvent; + + public NvHostGpuDeviceFile(ServiceCtx context) : base(context) + { + _smExceptionBptIntReportEvent = new KEvent(context.Device.System); + _smExceptionBptPauseReportEvent = new KEvent(context.Device.System); + _errorNotifierEvent = new KEvent(context.Device.System); + } + + public override NvInternalResult Ioctl2(NvIoctl command, Span arguments, Span inlineInBuffer) + { + NvInternalResult result = NvInternalResult.NotImplemented; + + if (command.Type == NvIoctl.NvHostMagic) + { + switch (command.Number) + { + case 0x1b: + result = CallIoctlMethod(SubmitGpfifoEx, arguments, inlineInBuffer); + break; + } + } + + return result; + } + + public override NvInternalResult QueryEvent(out int eventHandle, uint eventId) + { + // TODO: accurately represent and implement those events. + KEvent targetEvent = null; + + switch (eventId) + { + case 0x1: + targetEvent = _smExceptionBptIntReportEvent; + break; + case 0x2: + targetEvent = _smExceptionBptPauseReportEvent; + break; + case 0x3: + targetEvent = _errorNotifierEvent; + break; + } + + if (targetEvent != null) + { + if (Owner.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + else + { + eventHandle = 0; + + return NvInternalResult.InvalidInput; + } + + return NvInternalResult.Success; + } + + private NvInternalResult SubmitGpfifoEx(ref SubmitGpfifoArguments arguments, Span inlineData) + { + return SubmitGpfifo(ref arguments, inlineData); + } + } +} 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 000000000..8e5a15235 --- /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 000000000..fae91622c --- /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 000000000..425e665f4 --- /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 000000000..6a7e3da80 --- /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, Pack = 1)] + 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 148a640b5..4112a9fcc 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 0308912b8..000000000 --- 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 729464846..000000000 --- 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 f516588e8..000000000 --- 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 ef2f24e78..000000000 --- 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 e8cb5f0f5..000000000 --- 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 000000000..1aba53ca6 --- /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 000000000..bb2fd1cc0 --- /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 000000000..18cdde06b --- /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 000000000..19a997f43 --- /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/NvHostCtrlDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs new file mode 100644 index 000000000..e740350ed --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs @@ -0,0 +1,401 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Threading; +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 NvHostCtrlDeviceFile : NvDeviceFile + { + private const int EventsCount = 64; + + private bool _isProductionMode; + private NvHostSyncpt _syncpt; + private NvHostEvent[] _events; + private KEvent _dummyEvent; + + public NvHostCtrlDeviceFile(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]; + _dummyEvent = new KEvent(context.Device.System); + } + + public override NvInternalResult Ioctl(NvIoctl command, Span arguments) + { + NvInternalResult result = NvInternalResult.NotImplemented; + + if (command.Type == NvIoctl.NvHostCustomMagic) + { + switch (command.Number) + { + 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); + result = GetConfig(configArgument); + + if (result == NvInternalResult.Success) + { + 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; + } + } + + return result; + } + + public override NvInternalResult QueryEvent(out int eventHandle, uint eventId) + { + // TODO: implement SyncPts <=> KEvent logic accurately. For now we return a dummy event. + KEvent targetEvent = _dummyEvent; + + if (targetEvent != null) + { + if (Owner.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + else + { + eventHandle = 0; + + return NvInternalResult.InvalidInput; + } + + return NvInternalResult.Success; + } + + 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)) + { + byte[] settingBuffer = new byte[0x101]; + + if (nvSetting is string stringValue) + { + if (stringValue.Length > 0x100) + { + Logger.PrintError(LogClass.ServiceNv, $"{arguments.Domain}!{arguments.Parameter} String value size is too big!"); + } + else + { + settingBuffer = Encoding.ASCII.GetBytes(stringValue + "\0"); + } + } + else 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); + } + + Logger.PrintDebug(LogClass.ServiceNv, $"Got setting {arguments.Domain}!{arguments.Parameter}"); + + arguments.Configuration = settingBuffer; + + 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 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() { } + } +} 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 346e2dc76..000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlIoctl.cs +++ /dev/null @@ -1,400 +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 int 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 int SyncptRead(ServiceCtx context) - { - return SyncptReadMinOrMax(context, max: false); - } - - private static int SyncptIncr(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - - int id = context.Memory.ReadInt32(inputPosition); - - if ((uint)id >= NvHostSyncpt.SyncptsCount) - { - return NvResult.InvalidInput; - } - - GetUserCtx(context).Syncpt.Increment(id); - - return NvResult.Success; - } - - private static int SyncptWait(ServiceCtx context) - { - return SyncptWait(context, extended: false); - } - - private static int SyncptWaitEx(ServiceCtx context) - { - return SyncptWait(context, extended: true); - } - - private static int SyncptReadMax(ServiceCtx context) - { - return SyncptReadMinOrMax(context, max: true); - } - - private static int 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 NvResult.Success; - } - - return NvResult.NotAvailableInProduction; - } - - private static int EventWait(ServiceCtx context) - { - return EventWait(context, async: false); - } - - private static int EventWaitAsync(ServiceCtx context) - { - return EventWait(context, async: true); - } - - private static int 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 NvResult.Success; - } - - private static int 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 NvResult.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 NvResult.Success; - } - - private static int 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 NvResult.InvalidInput; - } - - int result; - - if (syncpt.MinCompare(args.Id, args.Thresh)) - { - result = NvResult.Success; - } - else if (args.Timeout == 0) - { - result = NvResult.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 = NvResult.Success; - } - else if (waitEvent.WaitOne(timeout)) - { - result = NvResult.Success; - } - else - { - result = NvResult.TimedOut; - } - } - - Logger.PrintDebug(LogClass.ServiceNv, "Resuming..."); - } - - if (extended) - { - context.Memory.WriteInt32(outputPosition + 0xc, syncpt.GetMin(args.Id)); - } - - return result; - } - - private static int 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 NvResult.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 NvResult.Success; - } - - if (!async) - { - args.Value = 0; - } - - if (args.Timeout == 0) - { - WriteArgs(); - - return NvResult.TryAgain; - } - - NvHostEvent Event; - - int result, eventIndex; - - if (async) - { - eventIndex = args.Value; - - if ((uint)eventIndex >= NvHostCtrlUserCtx.EventsCount) - { - return NvResult.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 = NvResult.TryAgain; - } - else - { - result = NvResult.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 000000000..3f97da1f7 --- /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/GetConfigurationArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/GetConfigurationArguments.cs new file mode 100644 index 000000000..3ee318a37 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/GetConfigurationArguments.cs @@ -0,0 +1,34 @@ +using System; +using System.Text; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl.Types +{ + class GetConfigurationArguments + { + public string Domain; + public string Parameter; + public byte[] Configuration; + + public static GetConfigurationArguments FromSpan(Span span) + { + string domain = Encoding.ASCII.GetString(span.Slice(0, 0x41)); + string parameter = Encoding.ASCII.GetString(span.Slice(0x41, 0x41)); + + GetConfigurationArguments result = new GetConfigurationArguments + { + Domain = domain.Substring(0, domain.IndexOf('\0')), + Parameter = parameter.Substring(0, parameter.IndexOf('\0')), + Configuration = span.Slice(0x82, 0x101).ToArray() + }; + + return result; + } + + public void CopyTo(Span span) + { + Encoding.ASCII.GetBytes(Domain + '\0').CopyTo(span.Slice(0, 0x41)); + Encoding.ASCII.GetBytes(Parameter + '\0').CopyTo(span.Slice(0x41, 0x41)); + Configuration.CopyTo(span.Slice(0x82, 0x101)); + } + } +} 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 8cfac5719..000000000 --- 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 401884c46..000000000 --- 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 49f573e26..000000000 --- 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 0b9d85cf9..000000000 --- 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 000000000..13ea89be0 --- /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 000000000..d04748ba3 --- /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/NvHostCtrlGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs new file mode 100644 index 000000000..ac7092a6a --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs @@ -0,0 +1,239 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types; +using System; +using System.Diagnostics; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu +{ + class NvHostCtrlGpuDeviceFile : NvDeviceFile + { + private static Stopwatch _pTimer = new Stopwatch(); + private static double _ticksToNs = (1.0 / Stopwatch.Frequency) * 1_000_000_000; + + private KEvent _errorEvent; + private KEvent _unknownEvent; + + public NvHostCtrlGpuDeviceFile(ServiceCtx context) : base(context) + { + _errorEvent = new KEvent(context.Device.System); + _unknownEvent = new KEvent(context.Device.System); + } + + static NvHostCtrlGpuDeviceFile() + { + _pTimer.Start(); + } + + public override NvInternalResult Ioctl(NvIoctl command, Span arguments) + { + NvInternalResult result = NvInternalResult.NotImplemented; + + if (command.Type == NvIoctl.NvGpuMagic) + { + switch (command.Number) + { + case 0x01: + result = CallIoctlMethod(ZcullGetCtxSize, arguments); + break; + case 0x02: + result = CallIoctlMethod(ZcullGetInfo, arguments); + break; + case 0x03: + result = CallIoctlMethod(ZbcSetTable, arguments); + break; + case 0x05: + result = CallIoctlMethod(GetCharacteristics, arguments); + break; + case 0x06: + result = CallIoctlMethod(GetTpcMasks, arguments); + break; + case 0x14: + result = CallIoctlMethod(GetActiveSlotMask, arguments); + break; + case 0x1c: + result = CallIoctlMethod(GetGpuTime, arguments); + break; + } + } + + return result; + } + + public override NvInternalResult Ioctl3(NvIoctl command, Span arguments, Span inlineOutBuffer) + { + NvInternalResult result = NvInternalResult.NotImplemented; + + if (command.Type == NvIoctl.NvGpuMagic) + { + switch (command.Number) + { + case 0x05: + result = CallIoctlMethod(GetCharacteristics, arguments, inlineOutBuffer); + break; + case 0x06: + result = CallIoctlMethod(GetTpcMasks, arguments, inlineOutBuffer); + break; + } + } + + return result; + } + + public override NvInternalResult QueryEvent(out int eventHandle, uint eventId) + { + // TODO: accurately represent and implement those events. + KEvent targetEvent = null; + + switch (eventId) + { + case 0x1: + targetEvent = _errorEvent; + break; + case 0x2: + targetEvent = _unknownEvent; + break; + } + + if (targetEvent != null) + { + if (Owner.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + else + { + eventHandle = 0; + + return NvInternalResult.InvalidInput; + } + + return NvInternalResult.Success; + } + + public override void Close() { } + + private NvInternalResult ZcullGetCtxSize(ref ZcullGetCtxSizeArguments arguments) + { + arguments.Size = 1; + + return NvInternalResult.Success; + } + + private NvInternalResult ZcullGetInfo(ref ZcullGetInfoArguments arguments) + { + arguments.WidthAlignPixels = 0x20; + arguments.HeightAlignPixels = 0x20; + arguments.PixelSquaresByAliquots = 0x400; + arguments.AliquotTotal = 0x800; + arguments.RegionByteMultiplier = 0x20; + arguments.RegionHeaderSize = 0x20; + arguments.SubregionHeaderSize = 0xc0; + arguments.SubregionWidthAlignPixels = 0x20; + arguments.SubregionHeightAlignPixels = 0x40; + arguments.SubregionCount = 0x10; + + return NvInternalResult.Success; + } + + private NvInternalResult ZbcSetTable(ref ZbcSetTableArguments arguments) + { + Logger.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult GetCharacteristics(ref GetCharacteristicsArguments arguments) + { + return GetCharacteristics(ref arguments, ref arguments.Characteristics); + } + + private NvInternalResult GetCharacteristics(ref GetCharacteristicsArguments arguments, ref GpuCharacteristics characteristics) + { + arguments.Header.BufferSize = 0xa0; + + characteristics.Arch = 0x120; + characteristics.Impl = 0xb; + characteristics.Rev = 0xa1; + characteristics.NumGpc = 0x1; + characteristics.L2CacheSize = 0x40000; + characteristics.OnBoardVideoMemorySize = 0x0; + characteristics.NumTpcPerGpc = 0x2; + characteristics.BusType = 0x20; + characteristics.BigPageSize = 0x20000; + characteristics.CompressionPageSize = 0x20000; + characteristics.PdeCoverageBitCount = 0x1b; + characteristics.AvailableBigPageSizes = 0x30000; + characteristics.GpcMask = 0x1; + characteristics.SmArchSmVersion = 0x503; + characteristics.SmArchSpaVersion = 0x503; + characteristics.SmArchWarpCount = 0x80; + characteristics.GpuVaBitCount = 0x28; + characteristics.Reserved = 0x0; + characteristics.Flags = 0x55; + characteristics.TwodClass = 0x902d; + characteristics.ThreedClass = 0xb197; + characteristics.ComputeClass = 0xb1c0; + characteristics.GpfifoClass = 0xb06f; + characteristics.InlineToMemoryClass = 0xa140; + characteristics.DmaCopyClass = 0xb0b5; + characteristics.MaxFbpsCount = 0x1; + characteristics.FbpEnMask = 0x0; + characteristics.MaxLtcPerFbp = 0x2; + characteristics.MaxLtsPerLtc = 0x1; + characteristics.MaxTexPerTpc = 0x0; + characteristics.MaxGpcCount = 0x1; + characteristics.RopL2EnMask0 = 0x21d70; + characteristics.RopL2EnMask1 = 0x0; + characteristics.ChipName = 0x6230326d67; + characteristics.GrCompbitStoreBaseHw = 0x0; + + arguments.Characteristics = characteristics; + + return NvInternalResult.Success; + } + + private NvInternalResult GetTpcMasks(ref GetTpcMasksArguments arguments) + { + return GetTpcMasks(ref arguments, ref arguments.TpcMask); + } + + private NvInternalResult GetTpcMasks(ref GetTpcMasksArguments arguments, ref int tpcMask) + { + if (arguments.MaskBufferSize != 0) + { + tpcMask = 3; + arguments.TpcMask = tpcMask; + } + + return NvInternalResult.Success; + } + + private NvInternalResult GetActiveSlotMask(ref GetActiveSlotMaskArguments arguments) + { + Logger.PrintStub(LogClass.ServiceNv); + + arguments.Slot = 0x07; + arguments.Mask = 0x01; + + return NvInternalResult.Success; + } + + private NvInternalResult GetGpuTime(ref GetGpuTimeArguments arguments) + { + arguments.Timestamp = GetPTimerNanoSeconds(); + + return NvInternalResult.Success; + } + + private static ulong GetPTimerNanoSeconds() + { + double ticks = _pTimer.ElapsedTicks; + + return (ulong)(ticks * _ticksToNs) & 0xff_ffff_ffff_ffff; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetActiveSlotMaskArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetActiveSlotMaskArguments.cs new file mode 100644 index 000000000..fd73be9e7 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetActiveSlotMaskArguments.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct GetActiveSlotMaskArguments + { + public int Slot; + public int Mask; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuGetCharacteristics.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetCharacteristicsArguments.cs similarity index 74% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuGetCharacteristics.cs rename to Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetCharacteristicsArguments.cs index 76aef2a78..5b44109a1 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuGetCharacteristics.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetCharacteristicsArguments.cs @@ -1,9 +1,10 @@ -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types { - struct NvGpuGpuGetCharacteristics + [StructLayout(LayoutKind.Sequential)] + struct GpuCharacteristics { - public long BufferSize; - public long BufferAddress; public int Arch; public int Impl; public int Rev; @@ -40,4 +41,17 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu public long ChipName; public long GrCompbitStoreBaseHw; } -} \ No newline at end of file + + struct CharacteristicsHeader + { + public long BufferSize; + public long BufferAddress; + } + + [StructLayout(LayoutKind.Sequential)] + struct GetCharacteristicsArguments + { + public CharacteristicsHeader Header; + public GpuCharacteristics Characteristics; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetGpuTimeArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetGpuTimeArguments.cs new file mode 100644 index 000000000..084ef71fb --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetGpuTimeArguments.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct GetGpuTimeArguments + { + public ulong Timestamp; + public ulong Reserved; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetTpcMasksArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetTpcMasksArguments.cs new file mode 100644 index 000000000..16ef2d6e1 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetTpcMasksArguments.cs @@ -0,0 +1,15 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types +{ + + [StructLayout(LayoutKind.Sequential)] + struct GetTpcMasksArguments + { + public int MaskBufferSize; + public int Reserved; + public long MaskBufferAddress; + public int TpcMask; + public int Padding; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs new file mode 100644 index 000000000..e21e437e8 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct ZbcSetTableArguments + { + // TODO + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetCtxSizeArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetCtxSizeArguments.cs new file mode 100644 index 000000000..1e668f867 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetCtxSizeArguments.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct ZcullGetCtxSizeArguments + { + public int Size; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuZcullGetInfo.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetInfoArguments.cs similarity index 68% rename from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuZcullGetInfo.cs rename to Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetInfoArguments.cs index ab17ca8b9..d0d152a3d 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuZcullGetInfo.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetInfoArguments.cs @@ -1,6 +1,9 @@ -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types { - struct NvGpuGpuZcullGetInfo + [StructLayout(LayoutKind.Sequential)] + struct ZcullGetInfoArguments { public int WidthAlignPixels; public int HeightAlignPixels; @@ -13,4 +16,4 @@ public int SubregionHeightAlignPixels; public int SubregionCount; } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs new file mode 100644 index 000000000..9345baeb5 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs @@ -0,0 +1,32 @@ +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices +{ + enum NvInternalResult : int + { + Success = 0, + OperationNotPermitted = -1, + NoEntry = -2, + Interrupted = -4, + IoError = -5, + DeviceNotFound = -6, + BadFileNumber = -9, + TryAgain = -11, + OutOfMemory = -12, + AccessDenied = -13, + BadAddress = -14, + Busy = -16, + NotADirectory = -20, + InvalidInput = -22, + FileTableOverflow = -23, + Unknown0x18 = -24, + NotSupported = -25, + FileTooBig = -27, + NoSpaceLeft = -28, + ReadOnlyAttribute = -30, + NotImplemented = -38, + InvalidState = -40, + Restart = -85, + InvalidAddress = -99, + TimedOut = -110, + Unknown0x72 = -114, + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs new file mode 100644 index 000000000..2ca847a31 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs @@ -0,0 +1,271 @@ +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.Memory; +using Ryujinx.HLE.HOS.Kernel.Process; +using System; +using System.Collections.Concurrent; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap +{ + internal class NvMapDeviceFile : NvDeviceFile + { + private const int FlagNotFreedYet = 1; + + private static ConcurrentDictionary _maps = new ConcurrentDictionary(); + + public NvMapDeviceFile(ServiceCtx context) : base(context) + { + IdDictionary dict = _maps.GetOrAdd(Owner, (key) => new IdDictionary()); + + dict.Add(0, new NvMapHandle()); + } + + public override NvInternalResult Ioctl(NvIoctl command, Span arguments) + { + NvInternalResult result = NvInternalResult.NotImplemented; + + if (command.Type == NvIoctl.NvMapCustomMagic) + { + switch (command.Number) + { + case 0x01: + result = CallIoctlMethod(Create, arguments); + break; + case 0x03: + result = CallIoctlMethod(FromId, arguments); + break; + case 0x04: + result = CallIoctlMethod(Alloc, arguments); + break; + case 0x05: + result = CallIoctlMethod(Free, arguments); + break; + case 0x09: + result = CallIoctlMethod(Param, arguments); + break; + case 0x0e: + result = CallIoctlMethod(GetId, arguments); + break; + case 0x02: + case 0x06: + case 0x07: + case 0x08: + case 0x0a: + case 0x0c: + case 0x0d: + case 0x0f: + case 0x10: + case 0x11: + result = NvInternalResult.NotSupported; + break; + } + } + + return result; + } + + private NvInternalResult Create(ref NvMapCreate arguments) + { + if (arguments.Size == 0) + { + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid size 0x{arguments.Size:x8}!"); + + return NvInternalResult.InvalidInput; + } + + int size = BitUtils.AlignUp(arguments.Size, NvGpuVmm.PageSize); + + arguments.Handle = CreateHandleFromMap(new NvMapHandle(size)); + + Logger.PrintInfo(LogClass.ServiceNv, $"Created map {arguments.Handle} with size 0x{size:x8}!"); + + return NvInternalResult.Success; + } + + private NvInternalResult FromId(ref NvMapFromId arguments) + { + NvMapHandle map = GetMapFromHandle(Owner, arguments.Id); + + if (map == null) + { + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!"); + + return NvInternalResult.InvalidInput; + } + + map.IncrementRefCount(); + + arguments.Handle = arguments.Id; + + return NvInternalResult.Success; + } + + private NvInternalResult Alloc(ref NvMapAlloc arguments) + { + NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle); + + if (map == null) + { + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!"); + + return NvInternalResult.InvalidInput; + } + + if ((arguments.Align & (arguments.Align - 1)) != 0) + { + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid alignment 0x{arguments.Align:x8}!"); + + return NvInternalResult.InvalidInput; + } + + if ((uint)arguments.Align < NvGpuVmm.PageSize) + { + arguments.Align = NvGpuVmm.PageSize; + } + + NvInternalResult result = NvInternalResult.Success; + + if (!map.Allocated) + { + map.Allocated = true; + + map.Align = arguments.Align; + map.Kind = (byte)arguments.Kind; + + int size = BitUtils.AlignUp(map.Size, NvGpuVmm.PageSize); + + long address = arguments.Address; + + if (address == 0) + { + // When the address is zero, we need to allocate + // our own backing memory for the NvMap. + // TODO: Is this allocation inside the transfer memory? + result = NvInternalResult.OutOfMemory; + } + + if (result == NvInternalResult.Success) + { + map.Size = size; + map.Address = address; + } + } + + return result; + } + + private NvInternalResult Free(ref NvMapFree arguments) + { + NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle); + + if (map == null) + { + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!"); + + return NvInternalResult.InvalidInput; + } + + if (map.DecrementRefCount() <= 0) + { + DeleteMapWithHandle(arguments.Handle); + + Logger.PrintInfo(LogClass.ServiceNv, $"Deleted map {arguments.Handle}!"); + + arguments.Address = map.Address; + arguments.Flags = 0; + } + else + { + arguments.Address = 0; + arguments.Flags = FlagNotFreedYet; + } + + arguments.Size = map.Size; + + return NvInternalResult.Success; + } + + private NvInternalResult Param(ref NvMapParam arguments) + { + NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle); + + if (map == null) + { + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!"); + + return NvInternalResult.InvalidInput; + } + + switch (arguments.Param) + { + case NvMapHandleParam.Size: arguments.Result = map.Size; break; + case NvMapHandleParam.Align: arguments.Result = map.Align; break; + case NvMapHandleParam.Heap: arguments.Result = 0x40000000; break; + case NvMapHandleParam.Kind: arguments.Result = map.Kind; break; + case NvMapHandleParam.Compr: arguments.Result = 0; break; + + // Note: Base is not supported and returns an error. + // Any other value also returns an error. + default: return NvInternalResult.InvalidInput; + } + + return NvInternalResult.Success; + } + + private NvInternalResult GetId(ref NvMapGetId arguments) + { + NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle); + + if (map == null) + { + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!"); + + return NvInternalResult.InvalidInput; + } + + arguments.Id = arguments.Handle; + + return NvInternalResult.Success; + } + + public override void Close() + { + // TODO: refcount NvMapDeviceFile instances and remove when closing + // _maps.TryRemove(GetOwner(), out _); + } + + private int CreateHandleFromMap(NvMapHandle map) + { + IdDictionary dict = _maps.GetOrAdd(Owner, (key) => + { + IdDictionary newDict = new IdDictionary(); + + newDict.Add(0, new NvMapHandle()); + + return newDict; + }); + + return dict.Add(map); + } + + private bool DeleteMapWithHandle(int handle) + { + if (_maps.TryGetValue(Owner, out IdDictionary dict)) + { + return dict.Delete(handle) != null; + } + + return false; + } + + public static NvMapHandle GetMapFromHandle(KProcess process, int handle, bool allowHandleZero = false) + { + if ((allowHandleZero || handle != 0) && _maps.TryGetValue(process, out IdDictionary dict)) + { + return dict.GetData(handle); + } + + return null; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapIoctl.cs deleted file mode 100644 index e46da4fde..000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapIoctl.cs +++ /dev/null @@ -1,300 +0,0 @@ -using ARMeilleure.Memory; -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.Memory; -using Ryujinx.HLE.HOS.Kernel.Process; -using System.Collections.Concurrent; - -namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap -{ - class NvMapIoctl - { - private const int FlagNotFreedYet = 1; - - private static ConcurrentDictionary _maps; - - static NvMapIoctl() - { - _maps = new ConcurrentDictionary(); - } - - public static int ProcessIoctl(ServiceCtx context, int cmd) - { - switch (cmd & 0xffff) - { - case 0x0101: return Create(context); - case 0x0103: return FromId(context); - case 0x0104: return Alloc (context); - case 0x0105: return Free (context); - case 0x0109: return Param (context); - case 0x010e: return GetId (context); - } - - Logger.PrintWarning(LogClass.ServiceNv, $"Unsupported Ioctl command 0x{cmd:x8}!"); - - return NvResult.NotSupported; - } - - private static int Create(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvMapCreate args = MemoryHelper.Read(context.Memory, inputPosition); - - if (args.Size == 0) - { - Logger.PrintWarning(LogClass.ServiceNv, $"Invalid size 0x{args.Size:x8}!"); - - return NvResult.InvalidInput; - } - - int size = BitUtils.AlignUp(args.Size, NvGpuVmm.PageSize); - - args.Handle = AddNvMap(context, new NvMapHandle(size)); - - Logger.PrintInfo(LogClass.ServiceNv, $"Created map {args.Handle} with size 0x{size:x8}!"); - - MemoryHelper.Write(context.Memory, outputPosition, args); - - return NvResult.Success; - } - - private static int FromId(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvMapFromId args = MemoryHelper.Read(context.Memory, inputPosition); - - NvMapHandle map = GetNvMap(context, args.Id); - - if (map == null) - { - Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{args.Handle:x8}!"); - - return NvResult.InvalidInput; - } - - map.IncrementRefCount(); - - args.Handle = args.Id; - - MemoryHelper.Write(context.Memory, outputPosition, args); - - return NvResult.Success; - } - - private static int Alloc(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvMapAlloc args = MemoryHelper.Read(context.Memory, inputPosition); - - NvMapHandle map = GetNvMap(context, args.Handle); - - if (map == null) - { - Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{args.Handle:x8}!"); - - return NvResult.InvalidInput; - } - - if ((args.Align & (args.Align - 1)) != 0) - { - Logger.PrintWarning(LogClass.ServiceNv, $"Invalid alignment 0x{args.Align:x8}!"); - - return NvResult.InvalidInput; - } - - if ((uint)args.Align < NvGpuVmm.PageSize) - { - args.Align = NvGpuVmm.PageSize; - } - - int result = NvResult.Success; - - if (!map.Allocated) - { - map.Allocated = true; - - map.Align = args.Align; - map.Kind = (byte)args.Kind; - - int size = BitUtils.AlignUp(map.Size, NvGpuVmm.PageSize); - - long address = args.Address; - - if (address == 0) - { - // When the address is zero, we need to allocate - // our own backing memory for the NvMap. - // TODO: Is this allocation inside the transfer memory? - result = NvResult.OutOfMemory; - } - - if (result == NvResult.Success) - { - map.Size = size; - map.Address = address; - } - } - - MemoryHelper.Write(context.Memory, outputPosition, args); - - return result; - } - - private static int Free(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvMapFree args = MemoryHelper.Read(context.Memory, inputPosition); - - NvMapHandle map = GetNvMap(context, args.Handle); - - if (map == null) - { - Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{args.Handle:x8}!"); - - return NvResult.InvalidInput; - } - - if (map.DecrementRefCount() <= 0) - { - DeleteNvMap(context, args.Handle); - - Logger.PrintInfo(LogClass.ServiceNv, $"Deleted map {args.Handle}!"); - - args.Address = map.Address; - args.Flags = 0; - } - else - { - args.Address = 0; - args.Flags = FlagNotFreedYet; - } - - args.Size = map.Size; - - MemoryHelper.Write(context.Memory, outputPosition, args); - - return NvResult.Success; - } - - private static int Param(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvMapParam args = MemoryHelper.Read(context.Memory, inputPosition); - - NvMapHandle map = GetNvMap(context, args.Handle); - - if (map == null) - { - Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{args.Handle:x8}!"); - - return NvResult.InvalidInput; - } - - switch ((NvMapHandleParam)args.Param) - { - case NvMapHandleParam.Size: args.Result = map.Size; break; - case NvMapHandleParam.Align: args.Result = map.Align; break; - case NvMapHandleParam.Heap: args.Result = 0x40000000; break; - case NvMapHandleParam.Kind: args.Result = map.Kind; break; - case NvMapHandleParam.Compr: args.Result = 0; break; - - // Note: Base is not supported and returns an error. - // Any other value also returns an error. - default: return NvResult.InvalidInput; - } - - MemoryHelper.Write(context.Memory, outputPosition, args); - - return NvResult.Success; - } - - private static int GetId(ServiceCtx context) - { - long inputPosition = context.Request.GetBufferType0x21().Position; - long outputPosition = context.Request.GetBufferType0x22().Position; - - NvMapGetId args = MemoryHelper.Read(context.Memory, inputPosition); - - NvMapHandle map = GetNvMap(context, args.Handle); - - if (map == null) - { - Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{args.Handle:x8}!"); - - return NvResult.InvalidInput; - } - - args.Id = args.Handle; - - MemoryHelper.Write(context.Memory, outputPosition, args); - - return NvResult.Success; - } - - private static int AddNvMap(ServiceCtx context, NvMapHandle map) - { - IdDictionary dict = _maps.GetOrAdd(context.Process, (key) => - { - IdDictionary newDict = new IdDictionary(); - - newDict.Add(0, new NvMapHandle()); - - return newDict; - }); - - return dict.Add(map); - } - - private static bool DeleteNvMap(ServiceCtx context, int handle) - { - if (_maps.TryGetValue(context.Process, out IdDictionary dict)) - { - return dict.Delete(handle) != null; - } - - return false; - } - - public static void InitializeNvMap(ServiceCtx context) - { - IdDictionary dict = _maps.GetOrAdd(context.Process, (key) =>new IdDictionary()); - - dict.Add(0, new NvMapHandle()); - } - - public static NvMapHandle GetNvMapWithFb(ServiceCtx context, int handle) - { - if (_maps.TryGetValue(context.Process, out IdDictionary dict)) - { - return dict.GetData(handle); - } - - return null; - } - - public static NvMapHandle GetNvMap(ServiceCtx context, int handle) - { - if (handle != 0 && _maps.TryGetValue(context.Process, out IdDictionary dict)) - { - return dict.GetData(handle); - } - - return null; - } - - public static void UnloadProcess(KProcess process) - { - _maps.TryRemove(process, out _); - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapAlloc.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapAlloc.cs index f449b6067..efc0f2aa3 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapAlloc.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapAlloc.cs @@ -1,5 +1,8 @@ +using System.Runtime.InteropServices; + namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { + [StructLayout(LayoutKind.Sequential)] struct NvMapAlloc { public int Handle; diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapCreate.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapCreate.cs index b1ccf1bc9..b47e46294 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapCreate.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapCreate.cs @@ -1,5 +1,8 @@ +using System.Runtime.InteropServices; + namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { + [StructLayout(LayoutKind.Sequential)] struct NvMapCreate { public int Size; diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFree.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFree.cs index 1d17c3a7d..d142b9f30 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFree.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFree.cs @@ -1,5 +1,8 @@ +using System.Runtime.InteropServices; + namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { + [StructLayout(LayoutKind.Sequential)] struct NvMapFree { public int Handle; diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFromId.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFromId.cs index 7f7f83ab8..2e559534d 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFromId.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFromId.cs @@ -1,5 +1,8 @@ +using System.Runtime.InteropServices; + namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { + [StructLayout(LayoutKind.Sequential)] struct NvMapFromId { public int Id; diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapGetId.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapGetId.cs index df8fff538..fe574eea5 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapGetId.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapGetId.cs @@ -1,5 +1,8 @@ +using System.Runtime.InteropServices; + namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { + [StructLayout(LayoutKind.Sequential)] struct NvMapGetId { public int Id; diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs index 9eb7efff9..61b73cba2 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { - enum NvMapHandleParam + enum NvMapHandleParam : int { Size = 1, Align = 2, diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapParam.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapParam.cs index c873a0d24..de5bab770 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapParam.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapParam.cs @@ -1,9 +1,12 @@ +using System.Runtime.InteropServices; + namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap { + [StructLayout(LayoutKind.Sequential)] struct NvMapParam { - public int Handle; - public int Param; - public int Result; + public int Handle; + public NvMapHandleParam Param; + public int Result; } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs new file mode 100644 index 000000000..058586949 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs @@ -0,0 +1,45 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nv +{ + [StructLayout(LayoutKind.Sequential)] + struct NvIoctl + { + 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; + private const int SizeBits = 14; + private const int DirectionBits = 2; + + private const int NumberShift = 0; + private const int TypeShift = NumberShift + NumberBits; + private const int SizeShift = TypeShift + TypeBits; + private const int DirectionShift = SizeShift + SizeBits; + + private const int NumberMask = (1 << NumberBits) - 1; + private const int TypeMask = (1 << TypeBits) - 1; + private const int SizeMask = (1 << SizeBits) - 1; + private const int DirectionMask = (1 << DirectionBits) - 1; + + [Flags] + public enum Direction : uint + { + None = 0, + Read = 1, + Write = 2, + } + + public uint RawValue; + + public uint Number => (RawValue >> NumberShift) & NumberMask; + public uint Type => (RawValue >> TypeShift) & TypeMask; + public uint Size => (RawValue >> SizeShift) & SizeMask; + public Direction DirectionValue => (Direction)((RawValue >> DirectionShift) & DirectionMask); + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/Types/NvFd.cs b/Ryujinx.HLE/HOS/Services/Nv/Types/NvFd.cs deleted file mode 100644 index b6c654e4e..000000000 --- a/Ryujinx.HLE/HOS/Services/Nv/Types/NvFd.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nv -{ - class NvFd - { - public string Name { get; private set; } - - public NvFd(string name) - { - Name = name; - } - } -} \ No newline at end of file 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 000000000..1458f482f --- /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; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/Types/NvIoctlNotImplementedException.cs b/Ryujinx.HLE/HOS/Services/Nv/Types/NvIoctlNotImplementedException.cs new file mode 100644 index 000000000..9404c18ce --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/Types/NvIoctlNotImplementedException.cs @@ -0,0 +1,55 @@ +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices; +using System; +using System.Text; + +namespace Ryujinx.HLE.HOS.Services.Nv.Types +{ + class NvIoctlNotImplementedException : Exception + { + public ServiceCtx Context { get; } + public NvDeviceFile DeviceFile { get; } + public NvIoctl Command { get; } + + public NvIoctlNotImplementedException(ServiceCtx context, NvDeviceFile deviceFile, NvIoctl command) + : this(context, deviceFile, command, "The ioctl is not implemented.") + { } + + public NvIoctlNotImplementedException(ServiceCtx context, NvDeviceFile deviceFile, NvIoctl command, string message) + : base(message) + { + Context = context; + DeviceFile = deviceFile; + Command = command; + } + + public override string Message + { + get + { + return base.Message + + Environment.NewLine + + Environment.NewLine + + BuildMessage(); + } + } + + private string BuildMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.AppendLine($"Device File: {DeviceFile.GetType().Name}"); + sb.AppendLine(); + + sb.AppendLine($"Ioctl (0x{Command.RawValue:x8})"); + sb.AppendLine($"\tNumber: 0x{Command.Number:x8}"); + sb.AppendLine($"\tType: 0x{Command.Type:x8}"); + sb.AppendLine($"\tSize: 0x{Command.Size:x8}"); + sb.AppendLine($"\tDirection: {Command.DirectionValue}"); + + sb.AppendLine("Guest Stack Trace:"); + sb.AppendLine(Context.Thread.GetGuestStackTrace()); + + return sb.ToString(); + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/Types/NvQueryEventNotImplementedException.cs b/Ryujinx.HLE/HOS/Services/Nv/Types/NvQueryEventNotImplementedException.cs new file mode 100644 index 000000000..b7a72eba0 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nv/Types/NvQueryEventNotImplementedException.cs @@ -0,0 +1,51 @@ +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices; +using System; +using System.Text; + +namespace Ryujinx.HLE.HOS.Services.Nv.Types +{ + class NvQueryEventNotImplementedException : Exception + { + public ServiceCtx Context { get; } + public NvDeviceFile DeviceFile { get; } + public uint EventId { get; } + + public NvQueryEventNotImplementedException(ServiceCtx context, NvDeviceFile deviceFile, uint eventId) + : this(context, deviceFile, eventId, "This query event is not implemented.") + { } + + public NvQueryEventNotImplementedException(ServiceCtx context, NvDeviceFile deviceFile, uint eventId, string message) + : base(message) + { + Context = context; + DeviceFile = deviceFile; + EventId = eventId; + } + + public override string Message + { + get + { + return base.Message + + Environment.NewLine + + Environment.NewLine + + BuildMessage(); + } + } + + private string BuildMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.AppendLine($"Device File: {DeviceFile.GetType().Name}"); + sb.AppendLine(); + + sb.AppendLine($"Event ID: (0x{EventId:x8})"); + + sb.AppendLine("Guest Stack Trace:"); + sb.AppendLine(Context.Thread.GetGuestStackTrace()); + + return sb.ToString(); + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nv/Types/NvResult.cs b/Ryujinx.HLE/HOS/Services/Nv/Types/NvResult.cs index 362a04502..1c9cae8ca 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/Types/NvResult.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/Types/NvResult.cs @@ -1,14 +1,30 @@ namespace Ryujinx.HLE.HOS.Services.Nv { - static class NvResult + enum NvResult : uint { - public const int NotAvailableInProduction = 196614; - public const int Success = 0; - public const int TryAgain = -11; - public const int OutOfMemory = -12; - public const int InvalidInput = -22; - public const int NotSupported = -25; - public const int Restart = -85; - public const int TimedOut = -110; + Success = 0, + NotImplemented = 1, + NotSupported = 2, + NotInitialized = 3, + InvalidParameter = 4, + Timeout = 5, + InsufficientMemory = 6, + ReadOnlyAttribute = 7, + InvalidState = 8, + InvalidAddress = 9, + InvalidSize = 10, + InvalidValue = 11, + AlreadyAllocated = 13, + Busy = 14, + ResourceError = 15, + CountMismatch = 16, + SharedMemoryTooSmall = 0x1000, + FileOperationFailed = 0x30003, + DirectoryOperationFailed = 0x30004, + NotAvailableInProduction = 0x30006, + IoctlFailed = 0x3000F, + AccessDenied = 0x30010, + FileNotFound = 0x30013, + ModuleNotPresent = 0xA000E, } } \ No newline at end of file