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
This commit is contained in:
parent
848cda1837
commit
9426ef3f06
75 changed files with 2798 additions and 2005 deletions
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@ using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Gal;
|
using Ryujinx.Graphics.Gal;
|
||||||
using Ryujinx.Graphics.Memory;
|
using Ryujinx.Graphics.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
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 Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -290,7 +290,7 @@ namespace Ryujinx.HLE.HOS.Services.Android
|
||||||
|
|
||||||
int bufferOffset = _bufferQueue[slot].Data.Buffer.Surfaces[0].Offset;
|
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;
|
long fbAddr = map.Address + bufferOffset;
|
||||||
|
|
||||||
|
@ -312,7 +312,7 @@ namespace Ryujinx.HLE.HOS.Services.Android
|
||||||
int right = crop.Right;
|
int right = crop.Right;
|
||||||
int bottom = crop.Bottom;
|
int bottom = crop.Bottom;
|
||||||
|
|
||||||
NvGpuVmm vmm = NvGpuASIoctl.GetASCtx(context).Vmm;
|
NvGpuVmm vmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(context.Process).Vmm;
|
||||||
|
|
||||||
_renderer.QueueAction(() =>
|
_renderer.QueueAction(() =>
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,104 +1,301 @@
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
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.Process;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
|
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.NvDrvServices.NvMap;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Nv.Types;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv
|
namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
{
|
{
|
||||||
[Service("nvdrv")]
|
[Service("nvdrv")]
|
||||||
[Service("nvdrv:a")]
|
[Service("nvdrv:a")]
|
||||||
|
[Service("nvdrv:s")]
|
||||||
|
[Service("nvdrv:t")]
|
||||||
class INvDrvServices : IpcService
|
class INvDrvServices : IpcService
|
||||||
{
|
{
|
||||||
private delegate int IoctlProcessor(ServiceCtx context, int cmd);
|
private static Dictionary<string, Type> _deviceFileRegistry =
|
||||||
|
new Dictionary<string, Type>()
|
||||||
private static Dictionary<string, IoctlProcessor> _ioctlProcessors =
|
|
||||||
new Dictionary<string, IoctlProcessor>()
|
|
||||||
{
|
{
|
||||||
{ "/dev/nvhost-as-gpu", ProcessIoctlNvGpuAS },
|
{ "/dev/nvmap", typeof(NvMapDeviceFile) },
|
||||||
{ "/dev/nvhost-ctrl", ProcessIoctlNvHostCtrl },
|
{ "/dev/nvhost-ctrl", typeof(NvHostCtrlDeviceFile) },
|
||||||
{ "/dev/nvhost-ctrl-gpu", ProcessIoctlNvGpuGpu },
|
{ "/dev/nvhost-ctrl-gpu", typeof(NvHostCtrlGpuDeviceFile) },
|
||||||
{ "/dev/nvhost-gpu", ProcessIoctlNvHostChannel },
|
{ "/dev/nvhost-as-gpu", typeof(NvHostAsGpuDeviceFile) },
|
||||||
{ "/dev/nvhost-nvdec", ProcessIoctlNvHostChannel },
|
{ "/dev/nvhost-gpu", typeof(NvHostGpuDeviceFile) },
|
||||||
{ "/dev/nvhost-vic", ProcessIoctlNvHostChannel },
|
//{ "/dev/nvhost-msenc", typeof(NvHostChannelDeviceFile) },
|
||||||
{ "/dev/nvmap", ProcessIoctlNvMap }
|
{ "/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)
|
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)]
|
[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)
|
public ResultCode Open(ServiceCtx context)
|
||||||
{
|
{
|
||||||
long namePtr = context.Request.SendBuff[0].Position;
|
NvResult errorCode = EnsureInitialized();
|
||||||
|
int fd = -1;
|
||||||
|
|
||||||
string name = MemoryHelper.ReadAsciiString(context.Memory, namePtr);
|
if (errorCode == NvResult.Success)
|
||||||
|
{
|
||||||
|
long pathPtr = context.Request.SendBuff[0].Position;
|
||||||
|
|
||||||
int fd = Fds.Add(context.Process, new NvFd(name));
|
string path = MemoryHelper.ReadAsciiString(context.Memory, pathPtr);
|
||||||
|
|
||||||
|
fd = Open(context, path);
|
||||||
|
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
errorCode = NvResult.FileOperationFailed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context.ResponseData.Write(fd);
|
context.ResponseData.Write(fd);
|
||||||
context.ResponseData.Write(0);
|
context.ResponseData.Write((uint)errorCode);
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(1)]
|
[Command(1)]
|
||||||
// Ioctl(u32 fd, u32 rq_id, buffer<bytes, 0x21>) -> (u32 error_code, buffer<bytes, 0x22>)
|
// Ioctl(s32 fd, u32 ioctl_cmd, buffer<bytes, 0x21> in_args) -> (u32 error_code, buffer<bytes, 0x22> out_args)
|
||||||
[Command(11)] // 3.0.0+
|
|
||||||
// Ioctl2(u32, u32, buffer<bytes, 0x21>, buffer<bytes, 0x21>) -> (u32, buffer<bytes, 0x22>)
|
|
||||||
public ResultCode Ioctl(ServiceCtx context)
|
public ResultCode Ioctl(ServiceCtx context)
|
||||||
{
|
{
|
||||||
int fd = context.RequestData.ReadInt32();
|
NvResult errorCode = EnsureInitialized();
|
||||||
int cmd = context.RequestData.ReadInt32();
|
|
||||||
|
|
||||||
NvFd fdData = Fds.GetData<NvFd>(context.Process, fd);
|
if (errorCode == NvResult.Success)
|
||||||
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
if (_ioctlProcessors.TryGetValue(fdData.Name, out IoctlProcessor process))
|
|
||||||
{
|
{
|
||||||
result = process(context, cmd);
|
int fd = context.RequestData.ReadInt32();
|
||||||
}
|
NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>();
|
||||||
else if (!ServiceConfiguration.IgnoreMissingServices)
|
|
||||||
{
|
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
|
||||||
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((uint)errorCode);
|
||||||
context.ResponseData.Write(result);
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(2)]
|
[Command(2)]
|
||||||
// Close(u32 fd) -> u32 error_code
|
// Close(s32 fd) -> u32 error_code
|
||||||
public ResultCode Close(ServiceCtx context)
|
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;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
@ -110,33 +307,90 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
long transferMemSize = context.RequestData.ReadInt64();
|
long transferMemSize = context.RequestData.ReadInt64();
|
||||||
int transferMemHandle = context.Request.HandleDesc.ToCopy[0];
|
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;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(4)]
|
[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)
|
public ResultCode QueryEvent(ServiceCtx context)
|
||||||
{
|
{
|
||||||
int fd = context.RequestData.ReadInt32();
|
NvResult errorCode = EnsureInitialized();
|
||||||
int eventId = context.RequestData.ReadInt32();
|
|
||||||
|
|
||||||
// TODO: Use Fd/EventId, different channels have different events.
|
if (errorCode == NvResult.Success)
|
||||||
if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out int handle) != KernelResult.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;
|
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)]
|
[Command(8)]
|
||||||
// SetClientPID(u64, pid) -> u32 error_code
|
// SetClientPID(u64, pid) -> u32 error_code
|
||||||
public ResultCode SetClientPid(ServiceCtx context)
|
public ResultCode SetClientPid(ServiceCtx context)
|
||||||
|
@ -157,7 +411,105 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(13)]
|
[Command(10)] // 3.0.0+
|
||||||
|
// InitializeDevtools(u32, handle<copy>) -> u32 error_code;
|
||||||
|
public ResultCode InitializeDevtools(ServiceCtx context)
|
||||||
|
{
|
||||||
|
throw new ServiceNotImplementedException(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
[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)
|
||||||
|
{
|
||||||
|
NvResult errorCode = EnsureInitialized();
|
||||||
|
|
||||||
|
if (errorCode == NvResult.Success)
|
||||||
|
{
|
||||||
|
int fd = context.RequestData.ReadInt32();
|
||||||
|
NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>();
|
||||||
|
|
||||||
|
(long inlineInBufferPosition, long inlineInBufferSize) = context.Request.GetBufferType0x21(1);
|
||||||
|
|
||||||
|
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
|
||||||
|
|
||||||
|
Span<byte> inlineInBuffer = new Span<byte>(context.Memory.ReadBytes(inlineInBufferPosition, inlineInBufferSize));
|
||||||
|
|
||||||
|
if (errorCode == NvResult.Success)
|
||||||
|
{
|
||||||
|
errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile);
|
||||||
|
|
||||||
|
if (errorCode == NvResult.Success)
|
||||||
|
{
|
||||||
|
NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBuffer);
|
||||||
|
|
||||||
|
if (internalResult == NvInternalResult.NotImplemented)
|
||||||
|
{
|
||||||
|
throw new NvIoctlNotImplementedException(context, deviceFile, ioctlCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
errorCode = ConvertInternalErrorCode(internalResult);
|
||||||
|
|
||||||
|
if (errorCode == NvResult.Success && (ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
|
||||||
|
{
|
||||||
|
context.Memory.WriteBytes(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.ResponseData.Write((uint)errorCode);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(12)] // 3.0.0+
|
||||||
|
// Ioctl3(s32 fd, u32 ioctl_cmd, buffer<bytes, 0x21> in_args) -> (u32 error_code, buffer<bytes, 0x22> out_args, buffer<bytes, 0x22> inline_out_buffer)
|
||||||
|
public ResultCode Ioctl3(ServiceCtx context)
|
||||||
|
{
|
||||||
|
NvResult errorCode = EnsureInitialized();
|
||||||
|
|
||||||
|
if (errorCode == NvResult.Success)
|
||||||
|
{
|
||||||
|
int fd = context.RequestData.ReadInt32();
|
||||||
|
NvIoctl ioctlCommand = context.RequestData.ReadStruct<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);
|
||||||
|
|
||||||
|
if (errorCode == NvResult.Success)
|
||||||
|
{
|
||||||
|
NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBuffer);
|
||||||
|
|
||||||
|
if (internalResult == NvInternalResult.NotImplemented)
|
||||||
|
{
|
||||||
|
throw new NvIoctlNotImplementedException(context, deviceFile, ioctlCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
errorCode = ConvertInternalErrorCode(internalResult);
|
||||||
|
|
||||||
|
if (errorCode == NvResult.Success && (ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
|
||||||
|
{
|
||||||
|
context.Memory.WriteBytes(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
|
||||||
|
context.Memory.WriteBytes(inlineOutBufferPosition, inlineOutBuffer.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.ResponseData.Write((uint)errorCode);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(13)] // 3.0.0+
|
||||||
// FinishInitialize(unknown<8>)
|
// FinishInitialize(unknown<8>)
|
||||||
public ResultCode FinishInitialize(ServiceCtx context)
|
public ResultCode FinishInitialize(ServiceCtx context)
|
||||||
{
|
{
|
||||||
|
@ -165,72 +517,5 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int ProcessIoctlNvGpuAS(ServiceCtx context, int cmd)
|
|
||||||
{
|
|
||||||
return ProcessIoctl(context, cmd, NvGpuASIoctl.ProcessIoctl);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ProcessIoctlNvHostCtrl(ServiceCtx context, int cmd)
|
|
||||||
{
|
|
||||||
return ProcessIoctl(context, cmd, NvHostCtrlIoctl.ProcessIoctl);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ProcessIoctlNvGpuGpu(ServiceCtx context, int cmd)
|
|
||||||
{
|
|
||||||
return ProcessIoctl(context, cmd, NvGpuGpuIoctl.ProcessIoctl);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ProcessIoctlNvHostChannel(ServiceCtx context, int cmd)
|
|
||||||
{
|
|
||||||
return ProcessIoctl(context, cmd, NvHostChannelIoctl.ProcessIoctl);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ProcessIoctlNvMap(ServiceCtx context, int cmd)
|
|
||||||
{
|
|
||||||
return ProcessIoctl(context, cmd, NvMapIoctl.ProcessIoctl);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ProcessIoctl(ServiceCtx context, int cmd, IoctlProcessor processor)
|
|
||||||
{
|
|
||||||
if (CmdIn(cmd) && context.Request.GetBufferType0x21().Position == 0)
|
|
||||||
{
|
|
||||||
Logger.PrintError(LogClass.ServiceNv, "Input buffer is null!");
|
|
||||||
|
|
||||||
return NvResult.InvalidInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CmdOut(cmd) && context.Request.GetBufferType0x22().Position == 0)
|
|
||||||
{
|
|
||||||
Logger.PrintError(LogClass.ServiceNv, "Output buffer is null!");
|
|
||||||
|
|
||||||
return NvResult.InvalidInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
return processor(context, cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool CmdIn(int cmd)
|
|
||||||
{
|
|
||||||
return ((cmd >> 30) & 1) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool CmdOut(int cmd)
|
|
||||||
{
|
|
||||||
return ((cmd >> 31) & 1) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void UnloadProcess(KProcess process)
|
|
||||||
{
|
|
||||||
Fds.DeleteProcess(process);
|
|
||||||
|
|
||||||
NvGpuASIoctl.UnloadProcess(process);
|
|
||||||
|
|
||||||
NvHostChannelIoctl.UnloadProcess(process);
|
|
||||||
|
|
||||||
NvHostCtrlIoctl.UnloadProcess(process);
|
|
||||||
|
|
||||||
NvMapIoctl.UnloadProcess(process);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
80
Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs
Normal file
80
Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
|
|
||||||
{
|
|
||||||
struct NvGpuASUnmapBuffer
|
|
||||||
{
|
|
||||||
public long Offset;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu
|
|
||||||
{
|
|
||||||
struct NvGpuGpuGetActiveSlotMask
|
|
||||||
{
|
|
||||||
public int Slot;
|
|
||||||
public int Mask;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu
|
|
||||||
{
|
|
||||||
struct NvGpuGpuZcullGetCtxSize
|
|
||||||
{
|
|
||||||
public int Size;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,20 @@
|
||||||
|
using ARMeilleure.Memory;
|
||||||
using Ryujinx.Graphics.Memory;
|
using Ryujinx.Graphics.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
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; }
|
public NvGpuVmm Vmm { get; private set; }
|
||||||
|
|
||||||
private class Range
|
private class Range
|
||||||
{
|
{
|
||||||
public ulong Start { get; private set; }
|
public ulong Start { get; private set; }
|
||||||
public ulong End { get; private set; }
|
public ulong End { get; private set; }
|
||||||
|
|
||||||
public Range(long position, long size)
|
public Range(long position, long size)
|
||||||
{
|
{
|
||||||
|
@ -22,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
|
||||||
private class MappedMemory : Range
|
private class MappedMemory : Range
|
||||||
{
|
{
|
||||||
public long PhysicalAddress { get; private set; }
|
public long PhysicalAddress { get; private set; }
|
||||||
public bool VaAllocated { get; private set; }
|
public bool VaAllocated { get; private set; }
|
||||||
|
|
||||||
public MappedMemory(
|
public MappedMemory(
|
||||||
long position,
|
long position,
|
||||||
|
@ -38,9 +42,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
|
||||||
private SortedList<long, Range> _maps;
|
private SortedList<long, Range> _maps;
|
||||||
private SortedList<long, Range> _reservations;
|
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>();
|
_maps = new SortedList<long, Range>();
|
||||||
_reservations = new SortedList<long, Range>();
|
_reservations = new SortedList<long, Range>();
|
||||||
|
@ -197,4 +201,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
|
||||||
return ltRg;
|
return ltRg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum AddressSpaceFlags : uint
|
||||||
|
{
|
||||||
|
FixedOffset = 1,
|
||||||
|
RemapSubRange = 0x100,
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types
|
||||||
|
{
|
||||||
|
struct UnmapBufferArguments
|
||||||
|
{
|
||||||
|
public long Offset;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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() { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
||||||
{
|
{
|
||||||
enum NvChannelPriority
|
enum NvChannelPriority : uint
|
||||||
{
|
{
|
||||||
Low = 50,
|
Low = 50,
|
||||||
Medium = 100,
|
Medium = 100,
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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() { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
|
|
||||||
{
|
|
||||||
struct NvHostCtrlSyncptRead
|
|
||||||
{
|
|
||||||
public int Id;
|
|
||||||
public int Value;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
|
|
||||||
{
|
|
||||||
struct NvHostCtrlSyncptWait
|
|
||||||
{
|
|
||||||
public int Id;
|
|
||||||
public int Thresh;
|
|
||||||
public int Timeout;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 Arch;
|
||||||
public int Impl;
|
public int Impl;
|
||||||
public int Rev;
|
public int Rev;
|
||||||
|
@ -40,4 +41,17 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu
|
||||||
public long ChipName;
|
public long ChipName;
|
||||||
public long GrCompbitStoreBaseHw;
|
public long GrCompbitStoreBaseHw;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
struct CharacteristicsHeader
|
||||||
|
{
|
||||||
|
public long BufferSize;
|
||||||
|
public long BufferAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct GetCharacteristicsArguments
|
||||||
|
{
|
||||||
|
public CharacteristicsHeader Header;
|
||||||
|
public GpuCharacteristics Characteristics;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct ZbcSetTableArguments
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 WidthAlignPixels;
|
||||||
public int HeightAlignPixels;
|
public int HeightAlignPixels;
|
||||||
|
@ -13,4 +16,4 @@
|
||||||
public int SubregionHeightAlignPixels;
|
public int SubregionHeightAlignPixels;
|
||||||
public int SubregionCount;
|
public int SubregionCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
||||||
{
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
struct NvMapAlloc
|
struct NvMapAlloc
|
||||||
{
|
{
|
||||||
public int Handle;
|
public int Handle;
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
||||||
{
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
struct NvMapCreate
|
struct NvMapCreate
|
||||||
{
|
{
|
||||||
public int Size;
|
public int Size;
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
||||||
{
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
struct NvMapFree
|
struct NvMapFree
|
||||||
{
|
{
|
||||||
public int Handle;
|
public int Handle;
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
||||||
{
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
struct NvMapFromId
|
struct NvMapFromId
|
||||||
{
|
{
|
||||||
public int Id;
|
public int Id;
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
||||||
{
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
struct NvMapGetId
|
struct NvMapGetId
|
||||||
{
|
{
|
||||||
public int Id;
|
public int Id;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
||||||
{
|
{
|
||||||
enum NvMapHandleParam
|
enum NvMapHandleParam : int
|
||||||
{
|
{
|
||||||
Size = 1,
|
Size = 1,
|
||||||
Align = 2,
|
Align = 2,
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
||||||
{
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
struct NvMapParam
|
struct NvMapParam
|
||||||
{
|
{
|
||||||
public int Handle;
|
public int Handle;
|
||||||
public int Param;
|
public NvMapHandleParam Param;
|
||||||
public int Result;
|
public int Result;
|
||||||
}
|
}
|
||||||
}
|
}
|
45
Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs
Normal file
45
Ryujinx.HLE/HOS/Services/Nv/NvIoctl.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv
|
|
||||||
{
|
|
||||||
class NvFd
|
|
||||||
{
|
|
||||||
public string Name { get; private set; }
|
|
||||||
|
|
||||||
public NvFd(string name)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
11
Ryujinx.HLE/HOS/Services/Nv/Types/NvFence.cs
Normal file
11
Ryujinx.HLE/HOS/Services/Nv/Types/NvFence.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,30 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv
|
namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
{
|
{
|
||||||
static class NvResult
|
enum NvResult : uint
|
||||||
{
|
{
|
||||||
public const int NotAvailableInProduction = 196614;
|
Success = 0,
|
||||||
public const int Success = 0;
|
NotImplemented = 1,
|
||||||
public const int TryAgain = -11;
|
NotSupported = 2,
|
||||||
public const int OutOfMemory = -12;
|
NotInitialized = 3,
|
||||||
public const int InvalidInput = -22;
|
InvalidParameter = 4,
|
||||||
public const int NotSupported = -25;
|
Timeout = 5,
|
||||||
public const int Restart = -85;
|
InsufficientMemory = 6,
|
||||||
public const int TimedOut = -110;
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue