Start rewriting nvservices internals

TODO:

- nvgpu device interface
- nvhost generic device interface
This commit is contained in:
Thog 2019-10-28 03:03:37 +01:00
parent eee639d6ba
commit 6b4c1c82cc
No known key found for this signature in database
GPG key ID: 0CD291558FAFDBC6
48 changed files with 1742 additions and 1146 deletions

View file

@ -2,7 +2,7 @@ using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Memory;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
using System;
using System.Collections.Generic;
@ -290,7 +290,7 @@ namespace Ryujinx.HLE.HOS.Services.Android
int bufferOffset = _bufferQueue[slot].Data.Buffer.Surfaces[0].Offset;
NvMapHandle map = NvMapIoctl.GetNvMap(context, nvMapHandle);
NvMapHandle map = NvMapFileDevice.GetMapFromHandle(context.Process, nvMapHandle);
long fbAddr = map.Address + bufferOffset;
@ -312,7 +312,7 @@ namespace Ryujinx.HLE.HOS.Services.Android
int right = crop.Right;
int bottom = crop.Bottom;
NvGpuVmm vmm = NvGpuASIoctl.GetASCtx(context).Vmm;
NvGpuVmm vmm = NvHostAsGpuFileDevice.GetAddressSpaceContext(context.Process).Vmm;
_renderer.QueueAction(() =>
{

View file

@ -1,104 +1,296 @@
using ARMeilleure.Memory;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
using Ryujinx.HLE.HOS.Services.Nv.Types;
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Ryujinx.HLE.HOS.Services.Nv
{
[Service("nvdrv")]
[Service("nvdrv:a")]
[Service("nvdrv:s")]
[Service("nvdrv:t")]
class INvDrvServices : IpcService
{
private delegate int IoctlProcessor(ServiceCtx context, int cmd);
private static Dictionary<string, IoctlProcessor> _ioctlProcessors =
new Dictionary<string, IoctlProcessor>()
// TODO: everything
private static Dictionary<string, Type> _fileDeviceRegistry =
new Dictionary<string, Type>()
{
{ "/dev/nvhost-as-gpu", ProcessIoctlNvGpuAS },
{ "/dev/nvhost-ctrl", ProcessIoctlNvHostCtrl },
{ "/dev/nvhost-ctrl-gpu", ProcessIoctlNvGpuGpu },
{ "/dev/nvhost-gpu", ProcessIoctlNvHostChannel },
{ "/dev/nvhost-nvdec", ProcessIoctlNvHostChannel },
{ "/dev/nvhost-vic", ProcessIoctlNvHostChannel },
{ "/dev/nvmap", ProcessIoctlNvMap }
{ "/dev/nvhost-as-gpu", typeof(NvHostAsGpuFileDevice) },
{ "/dev/nvhost-ctrl", typeof(NvHostCtrlFileDevice) },
{ "/dev/nvhost-ctrl-gpu", typeof(NvHostCtrlGpuFileDevice) },
//{ "/dev/nvhost-gpu", typeof(NvMapFileDevice) },
//{ "/dev/nvhost-nvdec", typeof(NvMapFileDevice) },
//{ "/dev/nvhost-vic", typeof(NvMapFileDevice) },
{ "/dev/nvmap", typeof(NvMapFileDevice) }
};
public static GlobalStateTable Fds { get; private set; }
private KEvent _event;
private static IdDictionary _fileDeviceIdRegistry = new IdDictionary();
private KProcess _owner;
public INvDrvServices(ServiceCtx context)
{
_event = new KEvent(context.Device.System);
_owner = null;
}
static INvDrvServices()
{
Fds = new GlobalStateTable();
// TODO: dynamically generate _channelRegistry with attribute
}
private int Open(string path)
{
if (_fileDeviceRegistry.TryGetValue(path, out Type fileDeviceClass))
{
ConstructorInfo constructor = fileDeviceClass.GetConstructor(new Type[] { typeof(KProcess) });
NvFileDevice fileDevice = (NvFileDevice)constructor.Invoke(new object[] { _owner });
return _fileDeviceIdRegistry.Add(fileDevice);
}
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.GetDirectionValue();
uint ioctlSize = ioctlCommand.GetSizeValue();
bool isRead = (ioctlDirection & NvIoctl.Direction.Read) == NvIoctl.Direction.Read;
bool isWrite = (ioctlDirection & NvIoctl.Direction.Write) == NvIoctl.Direction.Write;
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 GetFileDeviceFromFd(int fd, out NvFileDevice fileDevice)
{
fileDevice = null;
if (fd < 0)
{
return NvResult.InvalidParameter;
}
fileDevice = _fileDeviceIdRegistry.GetData<NvFileDevice>(fd);
if (fileDevice == null)
{
Logger.PrintWarning(LogClass.ServiceNv, $"Invalid file descriptor {fd}");
return NvResult.NotImplemented;
}
if (fileDevice.GetOwner().Pid != _owner.Pid)
{
return NvResult.AccessDenied;
}
return NvResult.Success;
}
private NvResult EnsureInitialized()
{
if (_owner == null)
{
Logger.PrintWarning(LogClass.ServiceNv, "INvDrvServices is not initialized!");
return NvResult.NotInitialized;
}
return NvResult.Success;
}
private static NvResult ConvertInternalErrorCode(NvInternalResult errorCode)
{
switch (errorCode)
{
case NvInternalResult.Success:
return NvResult.Success;
case NvInternalResult.Unknown0x72:
return NvResult.AlreadyAllocated;
case NvInternalResult.TimedOut:
case NvInternalResult.TryAgain:
case NvInternalResult.Interrupted:
return NvResult.Timeout;
case NvInternalResult.InvalidAddress:
return NvResult.InvalidAddress;
case NvInternalResult.NotSupported:
case NvInternalResult.Unknown0x18:
return NvResult.NotSupported;
case NvInternalResult.InvalidState:
return NvResult.InvalidState;
case NvInternalResult.ReadOnlyAttribute:
return NvResult.ReadOnlyAttribute;
case NvInternalResult.NoSpaceLeft:
case NvInternalResult.FileTooBig:
return NvResult.InvalidSize;
case NvInternalResult.FileTableOverflow:
case NvInternalResult.BadFileNumber:
return NvResult.FileOperationFailed;
case NvInternalResult.InvalidInput:
return NvResult.InvalidValue;
case NvInternalResult.NotADirectory:
return NvResult.DirectoryOperationFailed;
case NvInternalResult.Busy:
return NvResult.Busy;
case NvInternalResult.BadAddress:
return NvResult.InvalidAddress;
case NvInternalResult.AccessDenied:
case NvInternalResult.OperationNotPermitted:
return NvResult.AccessDenied;
case NvInternalResult.OutOfMemory:
return NvResult.InsufficientMemory;
case NvInternalResult.DeviceNotFound:
return NvResult.ModuleNotPresent;
case NvInternalResult.IoError:
return NvResult.ResourceError;
default:
return NvResult.IoctlFailed;
}
}
[Command(0)]
// Open(buffer<bytes, 5> path) -> (u32 fd, u32 error_code)
// Open(buffer<bytes, 5> path) -> (s32 fd, u32 error_code)
public ResultCode Open(ServiceCtx context)
{
long namePtr = context.Request.SendBuff[0].Position;
NvResult errorCode = EnsureInitialized();
int fd = -1;
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(path);
if (fd == -1)
{
errorCode = NvResult.FileOperationFailed;
}
}
context.ResponseData.Write(fd);
context.ResponseData.Write(0);
context.ResponseData.Write((uint)errorCode);
return ResultCode.Success;
}
[Command(1)]
// Ioctl(u32 fd, u32 rq_id, buffer<bytes, 0x21>) -> (u32 error_code, buffer<bytes, 0x22>)
[Command(11)] // 3.0.0+
// Ioctl2(u32, u32, buffer<bytes, 0x21>, buffer<bytes, 0x21>) -> (u32, buffer<bytes, 0x22>)
// Ioctl(s32 fd, u32 ioctl_cmd, buffer<bytes, 0x21> in_args) -> (u32 error_code, buffer<bytes, 0x22> out_args)
public ResultCode Ioctl(ServiceCtx context)
{
int fd = context.RequestData.ReadInt32();
int cmd = context.RequestData.ReadInt32();
NvResult errorCode = EnsureInitialized();
NvFd fdData = Fds.GetData<NvFd>(context.Process, fd);
int result = 0;
if (_ioctlProcessors.TryGetValue(fdData.Name, out IoctlProcessor process))
if (errorCode == NvResult.Success)
{
result = process(context, cmd);
}
else if (!ServiceConfiguration.IgnoreMissingServices)
{
throw new NotImplementedException($"{fdData.Name} {cmd:x4}");
int fd = context.RequestData.ReadInt32();
NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>();
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
if (errorCode == NvResult.Success)
{
errorCode = GetFileDeviceFromFd(fd, out NvFileDevice fileDevice);
if (errorCode == NvResult.Success)
{
NvInternalResult internalResult = fileDevice.Ioctl(ioctlCommand, arguments);
if (internalResult == NvInternalResult.NotImplemented)
{
throw new NvIoctlNotImplementedException(context, fileDevice, ioctlCommand);
}
errorCode = ConvertInternalErrorCode(internalResult);
if (errorCode == NvResult.Success && (ioctlCommand.GetDirectionValue() & NvIoctl.Direction.Write) == NvIoctl.Direction.Write)
{
context.Memory.WriteBytes(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
}
}
}
}
// TODO: Verify if the error codes needs to be translated.
context.ResponseData.Write(result);
context.ResponseData.Write((uint)errorCode);
return ResultCode.Success;
}
[Command(2)]
// Close(u32 fd) -> u32 error_code
// Close(s32 fd) -> u32 error_code
public ResultCode Close(ServiceCtx context)
{
int fd = context.RequestData.ReadInt32();
NvResult errorCode = EnsureInitialized();
Fds.Delete(context.Process, fd);
if (errorCode == NvResult.Success)
{
int fd = context.RequestData.ReadInt32();
context.ResponseData.Write(0);
errorCode = GetFileDeviceFromFd(fd, out NvFileDevice fileDevice);
if (errorCode == NvResult.Success)
{
fileDevice.Close();
}
}
context.ResponseData.Write((uint)errorCode);
return ResultCode.Success;
}
@ -110,127 +302,123 @@ namespace Ryujinx.HLE.HOS.Services.Nv
long transferMemSize = context.RequestData.ReadInt64();
int transferMemHandle = context.Request.HandleDesc.ToCopy[0];
NvMapIoctl.InitializeNvMap(context);
_owner = context.Process;
context.ResponseData.Write(0);
context.ResponseData.Write((uint)NvResult.Success);
return ResultCode.Success;
}
[Command(4)]
// QueryEvent(u32 fd, u32 event_id) -> (u32, handle<copy, event>)
// QueryEvent(s32 fd, u32 event_id) -> (u32, handle<copy, event>)
public ResultCode QueryEvent(ServiceCtx context)
{
int fd = context.RequestData.ReadInt32();
int eventId = context.RequestData.ReadInt32();
NvResult errorCode = EnsureInitialized();
// TODO: Use Fd/EventId, different channels have different events.
if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out int handle) != KernelResult.Success)
if (errorCode == NvResult.Success)
{
throw new InvalidOperationException("Out of handles!");
int fd = context.RequestData.ReadInt32();
uint eventId = context.RequestData.ReadUInt32();
errorCode = GetFileDeviceFromFd(fd, out NvFileDevice fileDevice);
if (errorCode == NvResult.Success)
{
errorCode = ConvertInternalErrorCode(fileDevice.QueryEvent(out int eventHandle, eventId));
if (errorCode == NvResult.Success)
{
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(eventHandle);
}
}
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
context.ResponseData.Write(0);
context.ResponseData.Write((uint)errorCode);
return ResultCode.Success;
}
[Command(5)]
// MapSharedMemory(s32 fd, u32 argument, handle<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 = GetFileDeviceFromFd(fd, out NvFileDevice fileDevice);
if (errorCode == NvResult.Success)
{
KSharedMemory sharedMemory = context.Process.HandleTable.GetObject<KSharedMemory>(sharedMemoryHandle);
errorCode = ConvertInternalErrorCode(fileDevice.MapSharedMemory(sharedMemory, argument));
}
}
context.ResponseData.Write((uint)errorCode);
return ResultCode.Success;
}
[Command(6)]
// GetStatus() -> (unknown<0x20>, u32 error_code)
public ResultCode GetStatus(ServiceCtx context)
{
throw new ServiceNotImplementedException(context);
}
[Command(7)]
// ForceSetClientPid(u64) -> u32 error_code
public ResultCode ForceSetClientPid(ServiceCtx context)
{
throw new ServiceNotImplementedException(context);
}
[Command(8)]
// SetClientPID(u64, pid) -> u32 error_code
public ResultCode SetClientPid(ServiceCtx context)
{
long pid = context.RequestData.ReadInt64();
context.ResponseData.Write(0);
return ResultCode.Success;
throw new ServiceNotImplementedException(context);
}
[Command(9)]
// DumpGraphicsMemoryInfo()
public ResultCode DumpGraphicsMemoryInfo(ServiceCtx context)
{
Logger.PrintStub(LogClass.ServiceNv);
return ResultCode.Success;
throw new ServiceNotImplementedException(context);
}
[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> in_additional_buffer) -> (u32 error_code, buffer<bytes, 0x22> out_args)
public ResultCode Ioctl2(ServiceCtx context)
{
throw new ServiceNotImplementedException(context);
}
[Command(12)] // 3.0.0+
// Ioctl2(s32 fd, u32 ioctl_cmd, buffer<bytes, 0x21> in_args) -> (u32 error_code, buffer<bytes, 0x22> out_args, buffer<bytes, 0x22> out_additional_buffer)
public ResultCode Ioctl3(ServiceCtx context)
{
throw new ServiceNotImplementedException(context);
}
[Command(13)] // 3.0.0+
// FinishInitialize(unknown<8>)
public ResultCode FinishInitialize(ServiceCtx context)
{
Logger.PrintStub(LogClass.ServiceNv);
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);
throw new ServiceNotImplementedException(context);
}
}
}

View file

@ -0,0 +1,11 @@
using System;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices
{
class NvDeviceAttribute : Attribute
{
public readonly string Path;
public NvDeviceAttribute(string path) => Path = path;
}
}

View file

@ -0,0 +1,71 @@
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices
{
abstract class NvFileDevice : IDisposable
{
protected KProcess _owner;
public NvFileDevice(KProcess owner)
{
_owner = owner;
}
public KProcess GetOwner()
{
return _owner;
}
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> inBuffer)
{
return NvInternalResult.NotImplemented;
}
public virtual NvInternalResult Ioctl3(NvIoctl command, Span<byte> arguments, Span<byte> outBuffer)
{
return NvInternalResult.NotImplemented;
}
protected delegate NvInternalResult IoctlProcessor<T>(ref T arguments);
protected delegate NvInternalResult IoctlProcessorSpan<T>(Span<T> arguments);
protected static NvInternalResult CallIoctlMethod<T>(IoctlProcessor<T> callback, Span<byte> arguments) where T : struct
{
Debug.Assert(arguments.Length == Marshal.SizeOf<T>());
return callback(ref MemoryMarshal.Cast<byte, T>(arguments)[0]);
}
protected static NvInternalResult CallIoctlMethod<T>(IoctlProcessorSpan<T> callback, Span<byte> arguments) where T : struct
{
return callback(MemoryMarshal.Cast<byte, T>(arguments));
}
public abstract void Close();
public void Dispose()
{
Close();
}
}
}

View file

@ -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 _);
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -1,7 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
{
struct NvGpuASUnmapBuffer
{
public long Offset;
}
}

View file

@ -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;
}
}
}

View file

@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu
{
struct NvGpuGpuGetActiveSlotMask
{
public int Slot;
public int Mask;
}
}

View file

@ -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;
}
}

View file

@ -1,7 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu
{
struct NvGpuGpuZcullGetCtxSize
{
public int Size;
}
}

View file

@ -0,0 +1,305 @@
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 NvHostAsGpuFileDevice : NvFileDevice
{
private const int FlagFixedOffset = 1;
private const int FlagRemapSubRange = 0x100;
private static ConcurrentDictionary<KProcess, AddressSpaceContext> _addressSpaceContextRegistry = new ConcurrentDictionary<KProcess, AddressSpaceContext>();
public NvHostAsGpuFileDevice(KProcess owner) : base(owner)
{
}
public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments)
{
NvInternalResult result = NvInternalResult.NotImplemented;
switch (command.GetNumberValue())
{
case 0x1:
result = CallIoctlMethod<BindChannelArguments>(BindChannel, arguments);
break;
case 0x2:
result = CallIoctlMethod<AllocSpaceArguments>(AllocSpace, arguments);
break;
case 0x3:
result = CallIoctlMethod<FreeSpaceArguments>(FreeSpace, arguments);
break;
case 0x5:
result = CallIoctlMethod<UnmapBufferArguments>(UnmapBuffer, arguments);
break;
case 0x6:
result = CallIoctlMethod<MapBufferExArguments>(MapBufferEx, arguments);
break;
case 0x8:
result = CallIoctlMethod<GetVaRegionsArguments>(GetVaRegions, arguments);
break;
case 0x9:
result = CallIoctlMethod<InitializeExArguments>(InitializeEx, arguments);
break;
case 0x14:
result = CallIoctlMethod<RemapArguments>(Remap, 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 & FlagFixedOffset) != 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 = NvMapFileDevice.GetMapFromHandle(_owner, arguments.NvMapHandle, true);
if (map == null)
{
Logger.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{arguments.NvMapHandle:x8}!");
return NvInternalResult.InvalidInput;
}
long pa;
if ((arguments.Flags & FlagRemapSubRange) != 0)
{
lock (addressSpaceContext)
{
if (addressSpaceContext.TryGetMapPhysicalAddress(arguments.Offset, out pa))
{
long va = arguments.Offset + arguments.BufferOffset;
pa += arguments.BufferOffset;
if (addressSpaceContext.Vmm.Map(pa, va, arguments.MappingSize) < 0)
{
string msg = string.Format(mapErrorMsg, va, arguments.MappingSize);
Logger.PrintWarning(LogClass.ServiceNv, msg);
return NvInternalResult.InvalidInput;
}
return NvInternalResult.Success;
}
else
{
Logger.PrintWarning(LogClass.ServiceNv, $"Address 0x{arguments.Offset:x16} not mapped!");
return NvInternalResult.InvalidInput;
}
}
}
pa = 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 vaAllocated = (arguments.Flags & FlagFixedOffset) == 0;
if (!vaAllocated)
{
if (addressSpaceContext.ValidateFixedBuffer(arguments.Offset, size))
{
arguments.Offset = addressSpaceContext.Vmm.Map(pa, arguments.Offset, size);
}
else
{
string msg = string.Format(mapErrorMsg, arguments.Offset, size);
Logger.PrintWarning(LogClass.ServiceNv, msg);
result = NvInternalResult.InvalidInput;
}
}
else
{
arguments.Offset = addressSpaceContext.Vmm.Map(pa, 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, pa, vaAllocated);
}
}
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 = NvMapFileDevice.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()
{
// TODO
}
public static AddressSpaceContext GetAddressSpaceContext(KProcess process)
{
return _addressSpaceContextRegistry.GetOrAdd(process, (key) => new AddressSpaceContext(process));
}
}
}

View file

@ -1,28 +1,32 @@
using ARMeilleure.Memory;
using Ryujinx.Graphics.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using System;
using System.Collections.Generic;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types
{
class NvGpuASCtx
class AddressSpaceContext
{
public NvGpuVmm Vmm { get; private set; }
private class Range
{
public ulong Start { get; private set; }
public ulong End { get; private set; }
public ulong Start { get; private set; }
public ulong End { get; private set; }
public Range(long position, long size)
{
Start = (ulong)position;
End = (ulong)size + Start;
End = (ulong)size + Start;
}
}
private class MappedMemory : Range
{
public long PhysicalAddress { get; private set; }
public bool VaAllocated { get; private set; }
public bool VaAllocated { get; private set; }
public MappedMemory(
long position,
@ -31,18 +35,18 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
bool vaAllocated) : base(position, size)
{
PhysicalAddress = physicalAddress;
VaAllocated = vaAllocated;
VaAllocated = vaAllocated;
}
}
private SortedList<long, Range> _maps;
private SortedList<long, Range> _reservations;
public NvGpuASCtx(ServiceCtx context)
public AddressSpaceContext(KProcess process)
{
Vmm = new NvGpuVmm(context.Memory);
Vmm = new NvGpuVmm(process.CpuMemory);
_maps = new SortedList<long, Range>();
_maps = new SortedList<long, Range>();
_reservations = new SortedList<long, Range>();
}
@ -135,7 +139,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
private Range BinarySearch(SortedList<long, Range> lst, long position)
{
int left = 0;
int left = 0;
int right = lst.Count - 1;
while (left <= right)
@ -168,7 +172,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
{
Range ltRg = null;
int left = 0;
int left = 0;
int right = lst.Count - 1;
while (left <= right)
@ -197,4 +201,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
return ltRg;
}
}
}
}

View file

@ -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 uint Flags;
public uint Padding;
public long Offset;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -1,6 +1,9 @@
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types
{
struct NvGpuASMapBufferEx
[StructLayout(LayoutKind.Sequential)]
struct MapBufferExArguments
{
public int Flags;
public int Kind;
@ -10,4 +13,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
public long MappingSize;
public long Offset;
}
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,7 @@
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types
{
struct UnmapBufferArguments
{
public long Offset;
}
}

View file

@ -2,7 +2,7 @@ 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.NvHostAsGpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
using System;
using System.Collections.Concurrent;
@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
_channels = new ConcurrentDictionary<KProcess, NvChannel>();
}
public static int ProcessIoctl(ServiceCtx context, int cmd)
public static NvInternalResult ProcessIoctl(ServiceCtx context, int cmd)
{
switch (cmd & 0xffff)
{
@ -44,14 +44,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
throw new NotImplementedException(cmd.ToString("x8"));
}
private static int Submit(ServiceCtx context)
private static NvInternalResult 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;
NvGpuVmm vmm = NvHostAsGpuFileDevice.GetAddressSpaceContext(context.Process).Vmm;
for (int index = 0; index < args.CmdBufsCount; index++)
{
@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
NvHostChannelCmdBuf cmdBuf = MemoryHelper.Read<NvHostChannelCmdBuf>(context.Memory, cmdBufOffset);
NvMapHandle map = NvMapIoctl.GetNvMap(context, cmdBuf.MemoryId);
NvMapHandle map = NvMapFileDevice.GetMapFromHandle(context.Process, cmdBuf.MemoryId);
int[] cmdBufData = new int[cmdBuf.WordsCount];
@ -73,10 +73,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
// TODO: Relocation, waitchecks, etc.
return NvResult.Success;
return NvInternalResult.Success;
}
private static int GetSyncpoint(ServiceCtx context)
private static NvInternalResult GetSyncpoint(ServiceCtx context)
{
// TODO
long inputPosition = context.Request.GetBufferType0x21().Position;
@ -88,10 +88,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
MemoryHelper.Write(context.Memory, outputPosition, args);
return NvResult.Success;
return NvInternalResult.Success;
}
private static int GetWaitBase(ServiceCtx context)
private static NvInternalResult GetWaitBase(ServiceCtx context)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position;
@ -102,10 +102,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
MemoryHelper.Write(context.Memory, outputPosition, args);
return NvResult.Success;
return NvInternalResult.Success;
}
private static int SetSubmitTimeout(ServiceCtx context)
private static NvInternalResult SetSubmitTimeout(ServiceCtx context)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
@ -115,29 +115,29 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
Logger.PrintStub(LogClass.ServiceNv);
return NvResult.Success;
return NvInternalResult.Success;
}
private static int MapBuffer(ServiceCtx context)
private static NvInternalResult 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;
NvGpuVmm vmm = NvHostAsGpuFileDevice.GetAddressSpaceContext(context.Process).Vmm;
for (int index = 0; index < args.NumEntries; index++)
{
int handle = context.Memory.ReadInt32(inputPosition + 0xc + index * 8);
NvMapHandle map = NvMapIoctl.GetNvMap(context, handle);
NvMapHandle map = NvMapFileDevice.GetMapFromHandle(context.Process, handle);
if (map == null)
{
Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{handle:x8}!");
return NvResult.InvalidInput;
return NvInternalResult.InvalidInput;
}
lock (map)
@ -151,28 +151,28 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
}
}
return NvResult.Success;
return NvInternalResult.Success;
}
private static int UnmapBuffer(ServiceCtx context)
private static NvInternalResult UnmapBuffer(ServiceCtx context)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
NvHostChannelMapBuffer args = MemoryHelper.Read<NvHostChannelMapBuffer>(context.Memory, inputPosition);
NvGpuVmm vmm = NvGpuASIoctl.GetASCtx(context).Vmm;
NvGpuVmm vmm = NvHostAsGpuFileDevice.GetAddressSpaceContext(context.Process).Vmm;
for (int index = 0; index < args.NumEntries; index++)
{
int handle = context.Memory.ReadInt32(inputPosition + 0xc + index * 8);
NvMapHandle map = NvMapIoctl.GetNvMap(context, handle);
NvMapHandle map = NvMapFileDevice.GetMapFromHandle(context.Process, handle);
if (map == null)
{
Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{handle:x8}!");
return NvResult.InvalidInput;
return NvInternalResult.InvalidInput;
}
lock (map)
@ -186,30 +186,30 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
}
}
return NvResult.Success;
return NvInternalResult.Success;
}
private static int SetUserData(ServiceCtx context)
private static NvInternalResult SetUserData(ServiceCtx context)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position;
Logger.PrintStub(LogClass.ServiceNv);
return NvResult.Success;
return NvInternalResult.Success;
}
private static int SetNvMap(ServiceCtx context)
private static NvInternalResult SetNvMap(ServiceCtx context)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position;
Logger.PrintStub(LogClass.ServiceNv);
return NvResult.Success;
return NvInternalResult.Success;
}
private static int SetTimeout(ServiceCtx context)
private static NvInternalResult SetTimeout(ServiceCtx context)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
@ -217,17 +217,17 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
Logger.PrintStub(LogClass.ServiceNv);
return NvResult.Success;
return NvInternalResult.Success;
}
private static int SubmitGpfifo(ServiceCtx context)
private static NvInternalResult 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;
NvGpuVmm vmm = NvHostAsGpuFileDevice.GetAddressSpaceContext(context.Process).Vmm;
for (int index = 0; index < args.NumEntries; index++)
{
@ -241,40 +241,40 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
MemoryHelper.Write(context.Memory, outputPosition, args);
return NvResult.Success;
return NvInternalResult.Success;
}
private static int AllocObjCtx(ServiceCtx context)
private static NvInternalResult AllocObjCtx(ServiceCtx context)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position;
Logger.PrintStub(LogClass.ServiceNv);
return NvResult.Success;
return NvInternalResult.Success;
}
private static int ZcullBind(ServiceCtx context)
private static NvInternalResult ZcullBind(ServiceCtx context)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position;
Logger.PrintStub(LogClass.ServiceNv);
return NvResult.Success;
return NvInternalResult.Success;
}
private static int SetErrorNotifier(ServiceCtx context)
private static NvInternalResult SetErrorNotifier(ServiceCtx context)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position;
Logger.PrintStub(LogClass.ServiceNv);
return NvResult.Success;
return NvInternalResult.Success;
}
private static int SetPriority(ServiceCtx context)
private static NvInternalResult SetPriority(ServiceCtx context)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
@ -290,34 +290,34 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
GetChannel(context).Timeslice = 5200; // Timeslice high priority in micro-seconds
break;
default:
return NvResult.InvalidInput;
return NvInternalResult.InvalidInput;
}
Logger.PrintStub(LogClass.ServiceNv);
// TODO: disable and preempt channel when GPU scheduler will be implemented.
return NvResult.Success;
return NvInternalResult.Success;
}
private static int AllocGpfifoEx2(ServiceCtx context)
private static NvInternalResult AllocGpfifoEx2(ServiceCtx context)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position;
Logger.PrintStub(LogClass.ServiceNv);
return NvResult.Success;
return NvInternalResult.Success;
}
private static int KickoffPbWithAttr(ServiceCtx context)
private static NvInternalResult 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;
NvGpuVmm vmm = NvHostAsGpuFileDevice.GetAddressSpaceContext(context.Process).Vmm;
for (int index = 0; index < args.NumEntries; index++)
{
@ -331,17 +331,17 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
MemoryHelper.Write(context.Memory, outputPosition, args);
return NvResult.Success;
return NvInternalResult.Success;
}
private static int SetTimeslice(ServiceCtx context)
private static NvInternalResult SetTimeslice(ServiceCtx context)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
int timeslice = context.Memory.ReadInt32(inputPosition);
if (timeslice < 1000 || timeslice > 50000)
{
return NvResult.InvalidInput;
return NvInternalResult.InvalidInput;
}
GetChannel(context).Timeslice = timeslice; // in micro-seconds
@ -350,7 +350,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
// TODO: disable and preempt channel when GPU scheduler will be implemented.
return NvResult.Success;
return NvInternalResult.Success;
}
private static void PushGpfifo(ServiceCtx context, NvGpuVmm vmm, long gpfifo)

View file

@ -0,0 +1,92 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl.Types;
using Ryujinx.HLE.HOS.Services.Settings;
using System;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
{
internal class NvHostCtrlFileDevice : NvFileDevice
{
private bool _isProductionMode;
public NvHostCtrlFileDevice(KProcess owner) : base(owner)
{
if (NxSettings.Settings.TryGetValue("nv!rmos_set_production_mode", out object productionModeSetting))
{
_isProductionMode = ((string)productionModeSetting) != "0"; // Default value is ""
}
}
public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments)
{
NvInternalResult result = NvInternalResult.NotImplemented;
switch (command.GetNumberValue())
{
case 0x1b:
// As Marshal cannot handle unaligned arrays, we do everything by hand here.
NvHostCtrlGetConfigurationArgument configArgument = NvHostCtrlGetConfigurationArgument.FromSpan(arguments);
result = GetConfig(configArgument);
if (result == NvInternalResult.Success)
{
configArgument.CopyTo(arguments);
}
break;
default:
break;
}
return result;
}
private NvInternalResult GetConfig(NvHostCtrlGetConfigurationArgument 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;
}
public override void Close()
{
// TODO
}
}
}

View file

@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
}
}
public static int ProcessIoctl(ServiceCtx context, int cmd)
public static NvInternalResult ProcessIoctl(ServiceCtx context, int cmd)
{
switch (cmd & 0xffff)
{
@ -43,12 +43,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
throw new NotImplementedException(cmd.ToString("x8"));
}
private static int SyncptRead(ServiceCtx context)
private static NvInternalResult SyncptRead(ServiceCtx context)
{
return SyncptReadMinOrMax(context, max: false);
}
private static int SyncptIncr(ServiceCtx context)
private static NvInternalResult SyncptIncr(ServiceCtx context)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
@ -56,30 +56,30 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
if ((uint)id >= NvHostSyncpt.SyncptsCount)
{
return NvResult.InvalidInput;
return NvInternalResult.InvalidInput;
}
GetUserCtx(context).Syncpt.Increment(id);
return NvResult.Success;
return NvInternalResult.Success;
}
private static int SyncptWait(ServiceCtx context)
private static NvInternalResult SyncptWait(ServiceCtx context)
{
return SyncptWait(context, extended: false);
}
private static int SyncptWaitEx(ServiceCtx context)
private static NvInternalResult SyncptWaitEx(ServiceCtx context)
{
return SyncptWait(context, extended: true);
}
private static int SyncptReadMax(ServiceCtx context)
private static NvInternalResult SyncptReadMax(ServiceCtx context)
{
return SyncptReadMinOrMax(context, max: true);
}
private static int GetConfig(ServiceCtx context)
private static NvInternalResult GetConfig(ServiceCtx context)
{
if (!_isProductionMode)
{
@ -123,23 +123,25 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
Logger.PrintDebug(LogClass.ServiceNv, $"Got setting {domain}!{name}");
}
return NvResult.Success;
return NvInternalResult.Success;
}
return NvResult.NotAvailableInProduction;
// NOTE: This actually return NotAvailableInProduction but this is directly translated as a InvalidInput before returning the ioctl.
//return NvInternalResult.NotAvailableInProduction;
return NvInternalResult.InvalidInput;
}
private static int EventWait(ServiceCtx context)
private static NvInternalResult EventWait(ServiceCtx context)
{
return EventWait(context, async: false);
}
private static int EventWaitAsync(ServiceCtx context)
private static NvInternalResult EventWaitAsync(ServiceCtx context)
{
return EventWait(context, async: true);
}
private static int EventRegister(ServiceCtx context)
private static NvInternalResult EventRegister(ServiceCtx context)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position;
@ -148,10 +150,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
Logger.PrintStub(LogClass.ServiceNv);
return NvResult.Success;
return NvInternalResult.Success;
}
private static int SyncptReadMinOrMax(ServiceCtx context, bool max)
private static NvInternalResult SyncptReadMinOrMax(ServiceCtx context, bool max)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position;
@ -160,7 +162,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
if ((uint)args.Id >= NvHostSyncpt.SyncptsCount)
{
return NvResult.InvalidInput;
return NvInternalResult.InvalidInput;
}
if (max)
@ -174,10 +176,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
MemoryHelper.Write(context.Memory, outputPosition, args);
return NvResult.Success;
return NvInternalResult.Success;
}
private static int SyncptWait(ServiceCtx context, bool extended)
private static NvInternalResult SyncptWait(ServiceCtx context, bool extended)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position;
@ -188,18 +190,18 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
if ((uint)args.Id >= NvHostSyncpt.SyncptsCount)
{
return NvResult.InvalidInput;
return NvInternalResult.InvalidInput;
}
int result;
NvInternalResult result;
if (syncpt.MinCompare(args.Id, args.Thresh))
{
result = NvResult.Success;
result = NvInternalResult.Success;
}
else if (args.Timeout == 0)
{
result = NvResult.TryAgain;
result = NvInternalResult.TryAgain;
}
else
{
@ -222,15 +224,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
{
waitEvent.WaitOne();
result = NvResult.Success;
result = NvInternalResult.Success;
}
else if (waitEvent.WaitOne(timeout))
{
result = NvResult.Success;
result = NvInternalResult.Success;
}
else
{
result = NvResult.TimedOut;
result = NvInternalResult.TimedOut;
}
}
@ -245,7 +247,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
return result;
}
private static int EventWait(ServiceCtx context, bool async)
private static NvInternalResult EventWait(ServiceCtx context, bool async)
{
long inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position;
@ -254,7 +256,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
if ((uint)args.Id >= NvHostSyncpt.SyncptsCount)
{
return NvResult.InvalidInput;
return NvInternalResult.InvalidInput;
}
void WriteArgs()
@ -270,7 +272,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
WriteArgs();
return NvResult.Success;
return NvInternalResult.Success;
}
if (!async)
@ -282,12 +284,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
{
WriteArgs();
return NvResult.TryAgain;
return NvInternalResult.TryAgain;
}
NvHostEvent Event;
int result, eventIndex;
NvInternalResult result;
int eventIndex;
if (async)
{
@ -295,7 +299,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
if ((uint)eventIndex >= NvHostCtrlUserCtx.EventsCount)
{
return NvResult.InvalidInput;
return NvInternalResult.InvalidInput;
}
Event = GetUserCtx(context).Events[eventIndex];
@ -325,11 +329,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
args.Value |= eventIndex;
result = NvResult.TryAgain;
result = NvInternalResult.TryAgain;
}
else
{
result = NvResult.InvalidInput;
result = NvInternalResult.InvalidInput;
}
WriteArgs();

View file

@ -0,0 +1,34 @@
using System;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl.Types
{
class NvHostCtrlGetConfigurationArgument
{
public string Domain;
public string Parameter;
public byte[] Configuration;
public static NvHostCtrlGetConfigurationArgument FromSpan(Span<byte> span)
{
string domain = Encoding.ASCII.GetString(span.Slice(0, 0x41));
string parameter = Encoding.ASCII.GetString(span.Slice(0x41, 0x41));
NvHostCtrlGetConfigurationArgument result = new NvHostCtrlGetConfigurationArgument
{
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));
}
}
}

View file

@ -0,0 +1,167 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types;
using System;
using System.Diagnostics;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu
{
class NvHostCtrlGpuFileDevice : NvFileDevice
{
private static Stopwatch _pTimer = new Stopwatch();
private static double _ticksToNs = (1.0 / Stopwatch.Frequency) * 1_000_000_000;
public NvHostCtrlGpuFileDevice(KProcess owner) : base(owner)
{
}
static NvHostCtrlGpuFileDevice()
{
_pTimer.Start();
}
public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments)
{
NvInternalResult result = NvInternalResult.NotImplemented;
switch (command.GetNumberValue())
{
case 0x1:
result = CallIoctlMethod<ZcullGetCtxSizeArguments>(ZcullGetCtxSize, arguments);
break;
case 0x2:
result = CallIoctlMethod<ZcullGetInfoArguments>(ZcullGetInfo, arguments);
break;
case 0x3:
result = CallIoctlMethod<ZbcSetTableArguments>(ZbcSetTable, arguments);
break;
case 0x5:
result = CallIoctlMethod<GetCharacteristicsArguments>(GetCharacteristics, arguments);
break;
case 0x6:
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 void Close()
{
// TODO
}
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)
{
arguments.BufferSize = 0xa0;
arguments.Characteristics.Arch = 0x120;
arguments.Characteristics.Impl = 0xb;
arguments.Characteristics.Rev = 0xa1;
arguments.Characteristics.NumGpc = 0x1;
arguments.Characteristics.L2CacheSize = 0x40000;
arguments.Characteristics.OnBoardVideoMemorySize = 0x0;
arguments.Characteristics.NumTpcPerGpc = 0x2;
arguments.Characteristics.BusType = 0x20;
arguments.Characteristics.BigPageSize = 0x20000;
arguments.Characteristics.CompressionPageSize = 0x20000;
arguments.Characteristics.PdeCoverageBitCount = 0x1b;
arguments.Characteristics.AvailableBigPageSizes = 0x30000;
arguments.Characteristics.GpcMask = 0x1;
arguments.Characteristics.SmArchSmVersion = 0x503;
arguments.Characteristics.SmArchSpaVersion = 0x503;
arguments.Characteristics.SmArchWarpCount = 0x80;
arguments.Characteristics.GpuVaBitCount = 0x28;
arguments.Characteristics.Reserved = 0x0;
arguments.Characteristics.Flags = 0x55;
arguments.Characteristics.TwodClass = 0x902d;
arguments.Characteristics.ThreedClass = 0xb197;
arguments.Characteristics.ComputeClass = 0xb1c0;
arguments.Characteristics.GpfifoClass = 0xb06f;
arguments.Characteristics.InlineToMemoryClass = 0xa140;
arguments.Characteristics.DmaCopyClass = 0xb0b5;
arguments.Characteristics.MaxFbpsCount = 0x1;
arguments.Characteristics.FbpEnMask = 0x0;
arguments.Characteristics.MaxLtcPerFbp = 0x2;
arguments.Characteristics.MaxLtsPerLtc = 0x1;
arguments.Characteristics.MaxTexPerTpc = 0x0;
arguments.Characteristics.MaxGpcCount = 0x1;
arguments.Characteristics.RopL2EnMask0 = 0x21d70;
arguments.Characteristics.RopL2EnMask1 = 0x0;
arguments.Characteristics.ChipName = 0x6230326d67;
arguments.Characteristics.GrCompbitStoreBaseHw = 0x0;
return NvInternalResult.Success;
}
private NvInternalResult GetTpcMasks(ref GetTpcMasksArguments arguments)
{
if (arguments.MaskBufferSize != 0)
{
arguments.TpcMask = 3;
}
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;
}
}
}

View file

@ -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;
}
}

View file

@ -1,9 +1,10 @@
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types
{
struct NvGpuGpuGetCharacteristics
[StructLayout(LayoutKind.Sequential)]
struct GpuCharacteristics
{
public long BufferSize;
public long BufferAddress;
public int Arch;
public int Impl;
public int Rev;
@ -40,4 +41,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu
public long ChipName;
public long GrCompbitStoreBaseHw;
}
}
[StructLayout(LayoutKind.Sequential)]
struct GetCharacteristicsArguments
{
public long BufferSize;
public long BufferAddress;
public GpuCharacteristics Characteristics;
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,14 @@
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;
}
}

View file

@ -0,0 +1,10 @@
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types
{
[StructLayout(LayoutKind.Sequential)]
struct ZbcSetTableArguments
{
}
}

View file

@ -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;
}
}

View file

@ -1,6 +1,9 @@
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types
{
struct NvGpuGpuZcullGetInfo
[StructLayout(LayoutKind.Sequential)]
struct ZcullGetInfoArguments
{
public int WidthAlignPixels;
public int HeightAlignPixels;
@ -13,4 +16,4 @@
public int SubregionHeightAlignPixels;
public int SubregionCount;
}
}
}

View file

@ -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,
}
}

View file

@ -0,0 +1,268 @@
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 NvMapFileDevice : NvFileDevice
{
private const int FlagNotFreedYet = 1;
private static ConcurrentDictionary<KProcess, IdDictionary> _maps = new ConcurrentDictionary<KProcess, IdDictionary>();
public NvMapFileDevice(KProcess owner) : base(owner)
{
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;
switch (command.GetNumberValue())
{
case 0x1:
result = CallIoctlMethod<NvMapCreate>(Create, arguments);
break;
case 0x3:
result = CallIoctlMethod<NvMapFromId>(FromId, arguments);
break;
case 0x4:
result = CallIoctlMethod<NvMapAlloc>(Alloc, arguments);
break;
case 0x5:
result = CallIoctlMethod<NvMapFree>(Free, arguments);
break;
case 0x9:
result = CallIoctlMethod<NvMapParam>(Param, arguments);
break;
case 0xe:
result = CallIoctlMethod<NvMapGetId>(GetId, arguments);
break;
case 0x2:
case 0x6:
case 0x7:
case 0x8:
case 0xa:
case 0xc:
case 0xd:
case 0xf:
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 NvMapFileDevice 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;
}
}
}

View file

@ -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 _);
}
}
}

View file

@ -1,5 +1,8 @@
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
{
[StructLayout(LayoutKind.Sequential)]
struct NvMapAlloc
{
public int Handle;

View file

@ -1,5 +1,8 @@
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
{
[StructLayout(LayoutKind.Sequential)]
struct NvMapCreate
{
public int Size;

View file

@ -1,5 +1,8 @@
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
{
[StructLayout(LayoutKind.Sequential)]
struct NvMapFree
{
public int Handle;

View file

@ -1,5 +1,8 @@
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
{
[StructLayout(LayoutKind.Sequential)]
struct NvMapFromId
{
public int Id;

View file

@ -1,5 +1,8 @@
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
{
[StructLayout(LayoutKind.Sequential)]
struct NvMapGetId
{
public int Id;

View file

@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
{
enum NvMapHandleParam
enum NvMapHandleParam : int
{
Size = 1,
Align = 2,

View file

@ -1,9 +1,12 @@
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
{
[StructLayout(LayoutKind.Sequential)]
struct NvMapParam
{
public int Handle;
public int Param;
public int Result;
public int Handle;
public NvMapHandleParam Param;
public int Result;
}
}

View file

@ -0,0 +1,54 @@
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nv
{
[StructLayout(LayoutKind.Sequential)]
struct NvIoctl
{
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 GetNumberValue()
{
return (RawValue >> NumberShift) & NumberMask;
}
public uint GetTypeValue()
{
return (RawValue >> TypeShift) & TypeMask;
}
public uint GetSizeValue()
{
return (RawValue >> SizeShift) & SizeMask;
}
public Direction GetDirectionValue()
{
return (Direction)((RawValue >> DirectionShift) & DirectionMask);
}
}
}

View file

@ -1,12 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Nv
{
class NvFd
{
public string Name { get; private set; }
public NvFd(string name)
{
Name = name;
}
}
}

View file

@ -0,0 +1,52 @@
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 NvFileDevice FileDevice { get; }
public NvIoctl Command { get; }
public NvIoctlNotImplementedException(ServiceCtx context, NvFileDevice fileDevice, NvIoctl command)
: this(context, fileDevice, command, "The ioctl is not implemented.")
{ }
public NvIoctlNotImplementedException(ServiceCtx context, NvFileDevice fileDevice, NvIoctl command, string message)
: base(message)
{
Context = context;
FileDevice = fileDevice;
Command = command;
}
public override string Message
{
get
{
return base.Message +
Environment.NewLine +
Environment.NewLine +
BuildMessage();
}
}
private string BuildMessage()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"Ioctl (0x{Command.RawValue:x8})");
sb.AppendLine($"\tNumber: 0x{Command.GetNumberValue():x8}");
sb.AppendLine($"\tType: 0x{Command.GetTypeValue():x8}");
sb.AppendLine($"\tSize: 0x{Command.GetSizeValue():x8}");
sb.AppendLine($"\tDirection: {Command.GetDirectionValue()}");
sb.AppendLine("Guest Stack Trace:");
sb.AppendLine(Context.Thread.GetGuestStackTrace());
return sb.ToString();
}
}
}

View file

@ -1,14 +1,30 @@
namespace Ryujinx.HLE.HOS.Services.Nv
{
static class NvResult
enum NvResult : uint
{
public const int NotAvailableInProduction = 196614;
public const int Success = 0;
public const int TryAgain = -11;
public const int OutOfMemory = -12;
public const int InvalidInput = -22;
public const int NotSupported = -25;
public const int Restart = -85;
public const int TimedOut = -110;
Success = 0,
NotImplemented = 1,
NotSupported = 2,
NotInitialized = 3,
InvalidParameter = 4,
Timeout = 5,
InsufficientMemory = 6,
ReadOnlyAttribute = 7,
InvalidState = 8,
InvalidAddress = 9,
InvalidSize = 10,
InvalidValue = 11,
AlreadyAllocated = 13,
Busy = 14,
ResourceError = 15,
CountMismatch = 16,
SharedMemoryTooSmall = 0x1000,
FileOperationFailed = 0x30003,
DirectoryOperationFailed = 0x30004,
NotAvailableInProduction = 0x30006,
IoctlFailed = 0x3000F,
AccessDenied = 0x30010,
FileNotFound = 0x30013,
ModuleNotPresent = 0xA000E,
}
}