aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Guillemard <me@thog.eu>2019-11-02 23:47:56 +0100
committerjduncanator <1518948+jduncanator@users.noreply.github.com>2019-11-03 09:47:56 +1100
commit9426ef3f06916f4206213b28b1ca162c851d4e07 (patch)
tree1968c0ac68da5759481881c0c792d5e5ed8c08d0
parent848cda1837334170f6658e73e8803e4824425810 (diff)
Rewrite nvservices (#800)
* Start rewriting nvservices internals TODO: - nvgpu device interface - nvhost generic device interface * Some clean up and fixes - Make sure to remove the fd of a closed channel. - NvFileDevice now doesn't implement Disposable as it was never used. - Rename NvHostCtrlGetConfigurationArgument to GetConfigurationArguments to follow calling convention. - Make sure to check every ioctls magic. * Finalize migration for ioctl standard variant TODO: ioctl2 migration * Implement SubmitGpfifoEx and fix nvdec * Implement Ioctl3 * Implement some ioctl3 required by recent games * Remove unused code and outdated comments * Return valid event handles with QueryEvent Also add an exception for unimplemented event ids. This commit doesn't implement accurately the events, this only define different events for different event ids. * Rename all occurance of FileDevice to DeviceFile * Restub SetClientPid to not cause regressions * Address comments * Remove GlobalStateTable * Address comments * Align variables in ioctl3 * Some missing alignments * GetVaRegionsArguments realign * Make Owner public in NvDeviceFile * Address LDj3SNuD's comments
-rw-r--r--Ryujinx.HLE/HOS/GlobalStateTable.cs70
-rw-r--r--Ryujinx.HLE/HOS/Services/Android/NvFlinger.cs6
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs499
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs80
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/NvGpuASIoctl.cs330
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASAllocSpace.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASMapBufferEx.cs13
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASRemap.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASUnmapBuffer.cs7
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/NvGpuGpuIoctl.cs190
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuGetActiveSlotMask.cs8
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuGetTpcMasks.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuZcullGetCtxSize.cs7
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs318
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs (renamed from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuAS/Types/NvGpuASCtx.cs)20
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceFlags.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AllocSpaceArguments.cs14
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/BindChannelArguments.cs10
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/FreeSpaceArguments.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/GetVaRegionsArguments.cs23
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/InitializeExArguments.cs16
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/MapBufferExArguments.cs16
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/RemapArguments.cs15
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/UnmapBufferArguments.cs7
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs347
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelIoctl.cs371
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs78
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocGpfifoExArguments.cs17
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/AllocObjCtxArguments.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/GetParameterArguments.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/MapCommandBufferArguments.cs21
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvChannelPriority.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelCmdBuf.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelGetParamArg.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelMapBuffer.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelSubmit.cs13
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/NvHostChannelSubmitGpfifo.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SetErrorNotifierArguments.cs13
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitArguments.cs21
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/SubmitGpfifoArguments.cs14
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/Types/ZcullBindArguments.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs401
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlIoctl.cs400
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/EventWaitArguments.cs13
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/GetConfigurationArguments.cs34
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlSyncPtRead.cs8
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlSyncPtWait.cs9
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlSyncPtWaitEx.cs10
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostCtrlUserCtx.cs19
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitArguments.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/SyncptWaitExArguments.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs239
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetActiveSlotMaskArguments.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetCharacteristicsArguments.cs (renamed from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuGetCharacteristics.cs)24
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetGpuTimeArguments.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/GetTpcMasksArguments.cs15
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs10
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetCtxSizeArguments.cs10
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZcullGetInfoArguments.cs (renamed from Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvGpuGpu/Types/NvGpuGpuZcullGetInfo.cs)9
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvInternalResult.cs32
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs271
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapIoctl.cs300
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapAlloc.cs3
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapCreate.cs3
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFree.cs3
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapFromId.cs3
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapGetId.cs3
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandleParam.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapParam.cs9
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs45
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/Types/NvFd.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/Types/NvFence.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/Types/NvIoctlNotImplementedException.cs55
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/Types/NvQueryEventNotImplementedException.cs51
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/Types/NvResult.cs34
75 files changed, 2780 insertions, 1987 deletions
diff --git a/Ryujinx.HLE/HOS/GlobalStateTable.cs b/Ryujinx.HLE/HOS/GlobalStateTable.cs
deleted file mode 100644
index 5e5e5ecf..00000000
--- 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<KProcess, IdDictionary> _dictByProcess;
-
- public GlobalStateTable()
- {
- _dictByProcess = new ConcurrentDictionary<KProcess, IdDictionary>();
- }
-
- 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<T>(KProcess process, int id)
- {
- if (_dictByProcess.TryGetValue(process, out IdDictionary dict))
- {
- return dict.GetData<T>(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<object> 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 5580c3be..63df78e5 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 da34421b..9e22d17e 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<string, IoctlProcessor> _ioctlProcessors =
- new Dictionary<string, IoctlProcessor>()
- {
- { "/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 }
+ private static Dictionary<string, Type> _deviceFileRegistry =
+ new Dictionary<string, Type>()
+ {
+ { "/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<byte> 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<byte>(outputData);
+ }
+ else if (isWrite)
+ {
+ byte[] outputData = new byte[outputDataSize];
+
+ arguments = new Span<byte>(outputData);
+ }
+ else
+ {
+ arguments = new Span<byte>(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<NvDeviceFile>(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<bytes, 5> path) -> (u32 fd, u32 error_code)
+ // Open(buffer<bytes, 5> path) -> (s32 fd, u32 error_code)
public ResultCode Open(ServiceCtx context)
{
- long namePtr = context.Request.SendBuff[0].Position;
+ NvResult errorCode = EnsureInitialized();
+ int fd = -1;
+
+ if (errorCode == NvResult.Success)
+ {
+ long pathPtr = context.Request.SendBuff[0].Position;
- string name = MemoryHelper.ReadAsciiString(context.Memory, namePtr);
+ string path = MemoryHelper.ReadAsciiString(context.Memory, pathPtr);
- int fd = Fds.Add(context.Process, new NvFd(name));
+ 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<bytes, 0x21>) -> (u32 error_code, buffer<bytes, 0x22>)
- [Command(11)] // 3.0.0+
- // Ioctl2(u32, u32, buffer<bytes, 0x21>, buffer<bytes, 0x21>) -> (u32, buffer<bytes, 0x22>)
+ // Ioctl(s32 fd, u32 ioctl_cmd, buffer<bytes, 0x21> in_args) -> (u32 error_code, buffer<bytes, 0x22> out_args)
public ResultCode Ioctl(ServiceCtx context)
{
- int fd = context.RequestData.ReadInt32();
- int cmd = context.RequestData.ReadInt32();
+ NvResult errorCode = EnsureInitialized();
- NvFd fdData = Fds.GetData<NvFd>(context.Process, fd);
+ if (errorCode == NvResult.Success)
+ {
+ int fd = context.RequestData.ReadInt32();
+ NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>();
- int result = 0;
+ errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
- if (_ioctlProcessors.TryGetValue(fdData.Name, out IoctlProcessor process))
- {
- result = process(context, cmd);
- }
- else if (!ServiceConfiguration.IgnoreMissingServices)
- {
- throw new NotImplementedException($"{fdData.Name} {cmd:x4}");
+ 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<copy, event>)
+ // QueryEvent(s32 fd, u32 event_id) -> (u32, handle<copy, event>)
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((uint)errorCode);
- context.ResponseData.Write(0);
+ return ResultCode.Success;
+ }
+
+ [Command(5)]
+ // MapSharedMemory(s32 fd, u32 argument, handle<copy, shared_memory>) -> 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<KSharedMemory>(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,80 +411,111 @@ namespace Ryujinx.HLE.HOS.Services.Nv
return ResultCode.Success;
}
- [Command(13)]
- // FinishInitialize(unknown<8>)
- public ResultCode FinishInitialize(ServiceCtx context)
+ [Command(10)] // 3.0.0+
+ // InitializeDevtools(u32, handle<copy>) -> u32 error_code;
+ public ResultCode InitializeDevtools(ServiceCtx context)
{
- Logger.PrintStub(LogClass.ServiceNv);
-
- return ResultCode.Success;
+ throw new ServiceNotImplementedException(context);
}
- private static int ProcessIoctlNvGpuAS(ServiceCtx context, int cmd)
+ [Command(11)] // 3.0.0+
+ // Ioctl2(s32 fd, u32 ioctl_cmd, buffer<bytes, 0x21> in_args, buffer<bytes, 0x21> inline_in_buffer) -> (u32 error_code, buffer<bytes, 0x22> out_args)
+ public ResultCode Ioctl2(ServiceCtx context)
{
- return ProcessIoctl(context, cmd, NvGpuASIoctl.ProcessIoctl);
- }
+ NvResult errorCode = EnsureInitialized();
- private static int ProcessIoctlNvHostCtrl(ServiceCtx context, int cmd)
- {
- return ProcessIoctl(context, cmd, NvHostCtrlIoctl.ProcessIoctl);
- }
+ if (errorCode == NvResult.Success)
+ {
+ int fd = context.RequestData.ReadInt32();
+ NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>();
- private static int ProcessIoctlNvGpuGpu(ServiceCtx context, int cmd)
- {
- return ProcessIoctl(context, cmd, NvGpuGpuIoctl.ProcessIoctl);
- }
+ (long inlineInBufferPosition, long inlineInBufferSize) = context.Request.GetBufferType0x21(1);
- private static int ProcessIoctlNvHostChannel(ServiceCtx context, int cmd)
- {
- return ProcessIoctl(context, cmd, NvHostChannelIoctl.ProcessIoctl);
- }
+ errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
- private static int ProcessIoctlNvMap(ServiceCtx context, int cmd)
- {
- return ProcessIoctl(context, cmd, NvMapIoctl.ProcessIoctl);
- }
+ Span<byte> inlineInBuffer = new Span<byte>(context.Memory.ReadBytes(inlineInBufferPosition, inlineInBufferSize));
- 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!");
+ if (errorCode == NvResult.Success)
+ {
+ errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile);
- return NvResult.InvalidInput;
- }
+ if (errorCode == NvResult.Success)
+ {
+ NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBuffer);
- if (CmdOut(cmd) && context.Request.GetBufferType0x22().Position == 0)
- {
- Logger.PrintError(LogClass.ServiceNv, "Output buffer is null!");
+ if (internalResult == NvInternalResult.NotImplemented)
+ {
+ throw new NvIoctlNotImplementedException(context, deviceFile, ioctlCommand);
+ }
- return NvResult.InvalidInput;
+ errorCode = ConvertInternalErrorCode(internalResult);
+
+ if (errorCode == NvResult.Success && (ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
+ {
+ context.Memory.WriteBytes(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
+ }
+ }
+ }
}
- return processor(context, cmd);
- }
+ context.ResponseData.Write((uint)errorCode);
- private static bool CmdIn(int cmd)
- {
- return ((cmd >> 30) & 1) != 0;
+ return ResultCode.Success;
}
- private static bool CmdOut(int cmd)
+ [Command(12)] // 3.0.0+
+ // Ioctl3(s32 fd, u32 ioctl_cmd, buffer<bytes, 0x21> in_args) -> (u32 error_code, buffer<bytes, 0x22> out_args, buffer<bytes, 0x22> inline_out_buffer)
+ public ResultCode Ioctl3(ServiceCtx context)
{
- return ((cmd >> 31) & 1) != 0;
- }
+ NvResult errorCode = EnsureInitialized();
- public static void UnloadProcess(KProcess process)
- {
- Fds.DeleteProcess(process);
+ if (errorCode == NvResult.Success)
+ {
+ int fd = context.RequestData.ReadInt32();
+ NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>();
+
+ (long inlineOutBufferPosition, long inlineOutBufferSize) = context.Request.GetBufferType0x22(1);
+
+ errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
+
+ Span<byte> inlineOutBuffer = new Span<byte>(context.Memory.ReadBytes(inlineOutBufferPosition, inlineOutBufferSize));
+
+ if (errorCode == NvResult.Success)
+ {
+ errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile);
- NvGpuASIoctl.UnloadProcess(process);
+ if (errorCode == NvResult.Success)
+ {
+ NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBuffer);
- NvHostChannelIoctl.UnloadProcess(process);
+ if (internalResult == NvInternalResult.NotImplemented)
+ {
+ throw new NvIoctlNotImplementedException(context, deviceFile, ioctlCommand);
+ }
- NvHostCtrlIoctl.UnloadProcess(process);
+ errorCode = ConvertInternalErrorCode(internalResult);
- NvMapIoctl.UnloadProcess(process);
+ 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)
+ {
+ Logger.PrintStub(LogClass.ServiceNv);
+
+ return ResultCode.Success;
}
}
} \ 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 00000000..73007c91
--- /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<byte> arguments)
+ {
+ return NvInternalResult.NotImplemented;
+ }
+
+ public virtual NvInternalResult Ioctl2(NvIoctl command, Span<byte> arguments, Span<byte> inlineInBuffer)
+ {
+ return NvInternalResult.NotImplemented;
+ }
+
+ public virtual NvInternalResult Ioctl3(NvIoctl command, Span<byte> arguments, Span<byte> inlineOutBuffer)
+ {
+ return NvInternalResult.NotImplemented;
+ }
+
+ protected delegate NvInternalResult IoctlProcessor<T>(ref T arguments);
+ protected delegate NvInternalResult IoctlProcessorSpan<T>(Span<T> arguments);
+ protected delegate NvInternalResult IoctlProcessorInline<T, T1>(ref T arguments, ref T1 inlineData);
+ protected delegate NvInternalResult IoctlProcessorInlineSpan<T, T1>(ref T arguments, Span<T1> inlineData);
+
+ protected static NvInternalResult CallIoctlMethod<T>(IoctlProcessor<T> callback, Span<byte> arguments) where T : struct
+ {
+ Debug.Assert(arguments.Length == Unsafe.SizeOf<T>());
+
+ return callback(ref MemoryMarshal.Cast<byte, T>(arguments)[0]);
+ }
+
+ protected static NvInternalResult CallIoctlMethod<T, T1>(IoctlProcessorInline<T, T1> callback, Span<byte> arguments, Span<byte> inlineBuffer) where T : struct where T1 : struct
+ {
+ Debug.Assert(arguments.Length == Unsafe.SizeOf<T>());
+ Debug.Assert(inlineBuffer.Length == Unsafe.SizeOf<T1>());
+
+ return callback(ref MemoryMarshal.Cast<byte, T>(arguments)[0], ref MemoryMarshal.Cast<byte, T1>(inlineBuffer)[0]);
+ }
+
+ protected static NvInternalResult CallIoctlMethod<T>(IoctlProcessorSpan<T> callback, Span<byte> arguments) where T : struct
+ {
+ return callback(MemoryMarshal.Cast<byte, T>(arguments));
+ }
+
+ protected static NvInternalResult CallIoctlMethod<T, T1>(IoctlProcessorInlineSpan<T, T1> callback, Span<byte> arguments, Span<byte> inlineBuffer) where T : struct where T1 : struct
+ {
+ Debug.Assert(arguments.Length == Unsafe.SizeOf<T>());
+
+ return callback(ref MemoryMarshal.Cast<byte, T>(arguments)[0], MemoryMarshal.Cast<byte, T1>(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 5c8d1fe0..00000000
--- 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<KProcess, NvGpuASCtx> _asCtxs;
-
- static NvGpuASIoctl()
- {
- _asCtxs = new ConcurrentDictionary<KProcess, NvGpuASCtx>();
- }
-
- 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<NvGpuASAllocSpace>(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<NvGpuASAllocSpace>(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<NvGpuASUnmapBuffer>(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<NvGpuASMapBufferEx>(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<NvGpuASRemap>(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 f0a0db35..00000000
--- 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 6ef80377..00000000
--- 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 0a6f8003..00000000
--- 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 63476b2f..00000000
--- 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 12f13153..00000000
--- 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<NvGpuGpuGetCharacteristics>(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<NvGpuGpuGetTpcMasks>(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 1b4c5345..00000000
--- 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 bc0966da..00000000
--- 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 8706d51d..00000000
--- 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 00000000..70783b43
--- /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<KProcess, AddressSpaceContext> _addressSpaceContextRegistry = new ConcurrentDictionary<KProcess, AddressSpaceContext>();
+
+ public NvHostAsGpuDeviceFile(ServiceCtx context) : base(context) { }
+
+ public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments)
+ {
+ NvInternalResult result = NvInternalResult.NotImplemented;
+
+ if (command.Type == NvIoctl.NvGpuAsMagic)
+ {
+ switch (command.Number)
+ {
+ case 0x01:
+ result = CallIoctlMethod<BindChannelArguments>(BindChannel, arguments);
+ break;
+ case 0x02:
+ result = CallIoctlMethod<AllocSpaceArguments>(AllocSpace, arguments);
+ break;
+ case 0x03:
+ result = CallIoctlMethod<FreeSpaceArguments>(FreeSpace, arguments);
+ break;
+ case 0x05:
+ result = CallIoctlMethod<UnmapBufferArguments>(UnmapBuffer, arguments);
+ break;
+ case 0x06:
+ result = CallIoctlMethod<MapBufferExArguments>(MapBufferEx, arguments);
+ break;
+ case 0x08:
+ result = CallIoctlMethod<GetVaRegionsArguments>(GetVaRegions, arguments);
+ break;
+ case 0x09:
+ result = CallIoctlMethod<InitializeExArguments>(InitializeEx, arguments);
+ break;
+ case 0x14:
+ result = CallIoctlMethod<RemapArguments>(Remap, arguments);
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ public override NvInternalResult Ioctl3(NvIoctl command, Span<byte> arguments, Span<byte> 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<GetVaRegionsArguments>(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<RemapArguments> 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
index 315fe353..5c283000 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<long, Range> _maps;
private SortedList<long, Range> _reservations;
- public NvGpuASCtx(ServiceCtx context)
+ public AddressSpaceContext(KProcess process)
{
- Vmm = new NvGpuVmm(context.Memory);
+ Vmm = new NvGpuVmm(process.CpuMemory);
_maps = new SortedList<long, Range>();
_reservations = new SortedList<long, Range>();
@@ -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 00000000..611cf78b
--- /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 00000000..73f746e2
--- /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 00000000..9c6568a3
--- /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 00000000..a853974b
--- /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 00000000..b3a9cf26
--- /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 00000000..882bda59
--- /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 00000000..02e058df
--- /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 00000000..0cf324b4
--- /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 00000000..1ef880af
--- /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 00000000..80a7ce80
--- /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<byte> arguments)
+ {
+ NvInternalResult result = NvInternalResult.NotImplemented;
+
+ if (command.Type == NvIoctl.NvHostCustomMagic)
+ {
+ switch (command.Number)
+ {
+ case 0x01:
+ result = Submit(arguments);
+ break;
+ case 0x02:
+ result = CallIoctlMethod<GetParameterArguments>(GetSyncpoint, arguments);
+ break;
+ case 0x03:
+ result = CallIoctlMethod<GetParameterArguments>(GetWaitBase, arguments);
+ break;
+ case 0x07:
+ result = CallIoctlMethod<uint>(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<int>(SetNvMapFd, arguments);
+ break;
+ case 0x03:
+ result = CallIoctlMethod<uint>(SetTimeout, arguments);
+ break;
+ case 0x08:
+ result = SubmitGpfifo(arguments);
+ break;
+ case 0x09:
+ result = CallIoctlMethod<AllocObjCtxArguments>(AllocObjCtx, arguments);
+ break;
+ case 0x0b:
+ result = CallIoctlMethod<ZcullBindArguments>(ZcullBind, arguments);
+ break;
+ case 0x0c:
+ result = CallIoctlMethod<SetErrorNotifierArguments>(SetErrorNotifier, arguments);
+ break;
+ case 0x0d:
+ result = CallIoctlMethod<NvChannelPriority>(SetPriority, arguments);
+ break;
+ case 0x18:
+ result = CallIoctlMethod<AllocGpfifoExArguments>(AllocGpfifoEx, arguments);
+ break;
+ case 0x1a:
+ result = CallIoctlMethod<AllocGpfifoExArguments>(AllocGpfifoEx2, arguments);
+ break;
+ case 0x1d:
+ result = CallIoctlMethod<uint>(SetTimeslice, arguments);
+ break;
+ }
+ }
+ else if (command.Type == NvIoctl.NvGpuMagic)
+ {
+ switch (command.Number)
+ {
+ case 0x14:
+ result = CallIoctlMethod<ulong>(SetUserData, arguments);
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ private NvInternalResult Submit(Span<byte> arguments)
+ {
+ int headerSize = Unsafe.SizeOf<SubmitArguments>();
+ SubmitArguments submitHeader = MemoryMarshal.Cast<byte, SubmitArguments>(arguments)[0];
+ Span<CommandBuffer> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBuffer>(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<byte> arguments)
+ {
+ int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
+ MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
+ Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(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<byte> arguments)
+ {
+ int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
+ MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
+ Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(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<byte> arguments)
+ {
+ int headerSize = Unsafe.SizeOf<SubmitGpfifoArguments>();
+ SubmitGpfifoArguments gpfifoSubmissionHeader = MemoryMarshal.Cast<byte, SubmitGpfifoArguments>(arguments)[0];
+ Span<long> gpfifoEntries = MemoryMarshal.Cast<byte, long>(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<long> 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 0d06e7e4..00000000
--- 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<KProcess, NvChannel> _channels;
-
- static NvHostChannelIoctl()
- {
- _channels = new ConcurrentDictionary<KProcess, NvChannel>();
- }
-
- 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<NvHostChannelSubmit>(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<NvHostChannelCmdBuf>(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<NvHostChannelGetParamArg>(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<NvHostChannelGetParamArg>(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<NvHostChannelMapBuffer>(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<NvHostChannelMapBuffer>(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<NvHostChannelSubmitGpfifo>(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<NvHostChannelSubmitGpfifo>(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 00000000..582ba50e
--- /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<byte> arguments, Span<byte> inlineInBuffer)
+ {
+ NvInternalResult result = NvInternalResult.NotImplemented;
+
+ if (command.Type == NvIoctl.NvHostMagic)
+ {
+ switch (command.Number)
+ {
+ case 0x1b:
+ result = CallIoctlMethod<SubmitGpfifoArguments, long>(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<long> 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 00000000..8e5a1523
--- /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 00000000..fae91622
--- /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 00000000..425e665f
--- /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 00000000..6a7e3da8
--- /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 148a640b..4112a9fc 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 0308912b..00000000
--- 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 72946484..00000000
--- 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 f516588e..00000000
--- 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 ef2f24e7..00000000
--- 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 e8cb5f0f..00000000
--- 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 00000000..1aba53ca
--- /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 00000000..bb2fd1cc
--- /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 00000000..18cdde06
--- /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 00000000..19a997f4
--- /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 00000000..e740350e
--- /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<byte> arguments)
+ {
+ NvInternalResult result = NvInternalResult.NotImplemented;
+
+ if (command.Type == NvIoctl.NvHostCustomMagic)
+ {
+ switch (command.Number)
+ {
+ case 0x14:
+ result = CallIoctlMethod<NvFence>(SyncptRead, arguments);
+ break;
+ case 0x15:
+ result = CallIoctlMethod<uint>(SyncptIncr, arguments);
+ break;
+ case 0x16:
+ result = CallIoctlMethod<SyncptWaitArguments>(SyncptWait, arguments);
+ break;
+ case 0x19:
+ result = CallIoctlMethod<SyncptWaitExArguments>(SyncptWaitEx, arguments);
+ break;
+ case 0x1a:
+ result = CallIoctlMethod<NvFence>(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<EventWaitArguments>(EventWait, arguments);
+ break;
+ case 0x1e:
+ result = CallIoctlMethod<EventWaitArguments>(EventWaitAsync, arguments);
+ break;
+ case 0x1f:
+ result = CallIoctlMethod<uint>(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 346e2dc7..00000000
--- 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<KProcess, NvHostCtrlUserCtx> _userCtxs;
-
- private static bool _isProductionMode = true;
-
- static NvHostCtrlIoctl()
- {
- _userCtxs = new ConcurrentDictionary<KProcess, NvHostCtrlUserCtx>();
-
- 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<NvHostCtrlSyncptRead>(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<NvHostCtrlSyncptWait>(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<NvHostCtrlSyncptWaitEx>(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 00000000..3f97da1f
--- /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 00000000..3ee318a3
--- /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<byte> 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<byte> 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 8cfac571..00000000
--- 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 401884c4..00000000
--- 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 49f573e2..00000000
--- 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 0b9d85cf..00000000
--- 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 00000000..13ea89be
--- /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 00000000..d04748ba
--- /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 00000000..ac7092a6
--- /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<byte> arguments)
+ {
+ NvInternalResult result = NvInternalResult.NotImplemented;
+
+ if (command.Type == NvIoctl.NvGpuMagic)
+ {
+ switch (command.Number)
+ {
+ case 0x01:
+ result = CallIoctlMethod<ZcullGetCtxSizeArguments>(ZcullGetCtxSize, arguments);
+ break;
+ case 0x02:
+ result = CallIoctlMethod<ZcullGetInfoArguments>(ZcullGetInfo, arguments);
+ break;
+ case 0x03:
+ result = CallIoctlMethod<ZbcSetTableArguments>(ZbcSetTable, arguments);
+ break;
+ case 0x05:
+ result = CallIoctlMethod<GetCharacteristicsArguments>(GetCharacteristics, arguments);
+ break;
+ case 0x06:
+ result = CallIoctlMethod<GetTpcMasksArguments>(GetTpcMasks, arguments);
+ break;
+ case 0x14:
+ result = CallIoctlMethod<GetActiveSlotMaskArguments>(GetActiveSlotMask, arguments);
+ break;
+ case 0x1c:
+ result = CallIoctlMethod<GetGpuTimeArguments>(GetGpuTime, arguments);
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ public override NvInternalResult Ioctl3(NvIoctl command, Span<byte> arguments, Span<byte> inlineOutBuffer)
+ {
+ NvInternalResult result = NvInternalResult.NotImplemented;
+
+ if (command.Type == NvIoctl.NvGpuMagic)
+ {
+ switch (command.Number)
+ {
+ case 0x05:
+ result = CallIoctlMethod<GetCharacteristicsArguments, GpuCharacteristics>(GetCharacteristics, arguments, inlineOutBuffer);
+ break;
+ case 0x06:
+ result = CallIoctlMethod<GetTpcMasksArguments, int>(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 00000000..fd73be9e
--- /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
index 76aef2a7..5b44109a 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 00000000..084ef71f
--- /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 00000000..16ef2d6e
--- /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 00000000..e21e437e
--- /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 00000000..1e668f86
--- /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
index ab17ca8b..d0d152a3 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 00000000..9345baeb
--- /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 00000000..2ca847a3
--- /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<KProcess, IdDictionary> _maps = new ConcurrentDictionary<KProcess, IdDictionary>();
+
+ 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<byte> arguments)
+ {
+ NvInternalResult result = NvInternalResult.NotImplemented;
+
+ if (command.Type == NvIoctl.NvMapCustomMagic)
+ {
+ switch (command.Number)
+ {
+ case 0x01:
+ result = CallIoctlMethod<NvMapCreate>(Create, arguments);
+ break;
+ case 0x03:
+ result = CallIoctlMethod<NvMapFromId>(FromId, arguments);
+ break;
+ case 0x04:
+ result = CallIoctlMethod<NvMapAlloc>(Alloc, arguments);
+ break;
+ case 0x05:
+ result = CallIoctlMethod<NvMapFree>(Free, arguments);
+ break;
+ case 0x09:
+ result = CallIoctlMethod<NvMapParam>(Param, arguments);
+ break;
+ case 0x0e:
+ result = CallIoctlMethod<NvMapGetId>(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<NvMapHandle>(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 e46da4fd..00000000
--- 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<KProcess, IdDictionary> _maps;
-
- static NvMapIoctl()
- {
- _maps = new ConcurrentDictionary<KProcess, IdDictionary>();
- }
-
- 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<NvMapCreate>(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<NvMapFromId>(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<NvMapAlloc>(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<NvMapFree>(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<NvMapParam>(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<NvMapGetId>(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<NvMapHandle>(handle);
- }
-
- return null;
- }
-
- public static NvMapHandle GetNvMap(ServiceCtx context, int handle)
- {
- if (handle != 0 && _maps.TryGetValue(context.Process, out IdDictionary dict))
- {
- return dict.GetData<NvMapHandle>(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 f449b606..efc0f2aa 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 b1ccf1bc..b47e4629 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 1d17c3a7..d142b9f3 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 7f7f83ab..2e559534 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 df8fff53..fe574eea 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 9eb7efff..61b73cba 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 c873a0d2..de5bab77 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 00000000..05858694
--- /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 b6c654e4..00000000
--- 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 00000000..1458f482
--- /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 00000000..9404c18c
--- /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 00000000..b7a72eba
--- /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 362a0450..1c9cae8c 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