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
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.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 = NvMapFileDevice.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 = NvHostAsGpuFileDevice.GetAddressSpaceContext(context.Process).Vmm;
_renderer.QueueAction(() => _renderer.QueueAction(() =>
{ {

View file

@ -1,104 +1,296 @@
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.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.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); // TODO: everything
private static Dictionary<string, Type> _fileDeviceRegistry =
private static Dictionary<string, IoctlProcessor> _ioctlProcessors = new Dictionary<string, Type>()
new Dictionary<string, IoctlProcessor>()
{ {
{ "/dev/nvhost-as-gpu", ProcessIoctlNvGpuAS }, { "/dev/nvhost-as-gpu", typeof(NvHostAsGpuFileDevice) },
{ "/dev/nvhost-ctrl", ProcessIoctlNvHostCtrl }, { "/dev/nvhost-ctrl", typeof(NvHostCtrlFileDevice) },
{ "/dev/nvhost-ctrl-gpu", ProcessIoctlNvGpuGpu }, { "/dev/nvhost-ctrl-gpu", typeof(NvHostCtrlGpuFileDevice) },
{ "/dev/nvhost-gpu", ProcessIoctlNvHostChannel }, //{ "/dev/nvhost-gpu", typeof(NvMapFileDevice) },
{ "/dev/nvhost-nvdec", ProcessIoctlNvHostChannel }, //{ "/dev/nvhost-nvdec", typeof(NvMapFileDevice) },
{ "/dev/nvhost-vic", ProcessIoctlNvHostChannel }, //{ "/dev/nvhost-vic", typeof(NvMapFileDevice) },
{ "/dev/nvmap", ProcessIoctlNvMap } { "/dev/nvmap", typeof(NvMapFileDevice) }
}; };
public static GlobalStateTable Fds { get; private set; } private static IdDictionary _fileDeviceIdRegistry = new IdDictionary();
private KProcess _owner;
private KEvent _event;
public INvDrvServices(ServiceCtx context) public INvDrvServices(ServiceCtx context)
{ {
_event = new KEvent(context.Device.System); _owner = null;
} }
static INvDrvServices() 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)] [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(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 = 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((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 = GetFileDeviceFromFd(fd, out NvFileDevice fileDevice);
if (errorCode == NvResult.Success)
{
fileDevice.Close();
}
}
context.ResponseData.Write((uint)errorCode);
return ResultCode.Success; return ResultCode.Success;
} }
@ -110,127 +302,123 @@ 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 = 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((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 = 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)] [Command(8)]
// SetClientPID(u64, pid) -> u32 error_code // SetClientPID(u64, pid) -> u32 error_code
public ResultCode SetClientPid(ServiceCtx context) public ResultCode SetClientPid(ServiceCtx context)
{ {
long pid = context.RequestData.ReadInt64(); throw new ServiceNotImplementedException(context);
context.ResponseData.Write(0);
return ResultCode.Success;
} }
[Command(9)] [Command(9)]
// DumpGraphicsMemoryInfo() // DumpGraphicsMemoryInfo()
public ResultCode DumpGraphicsMemoryInfo(ServiceCtx context) public ResultCode DumpGraphicsMemoryInfo(ServiceCtx context)
{ {
Logger.PrintStub(LogClass.ServiceNv); throw new ServiceNotImplementedException(context);
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> 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>) // FinishInitialize(unknown<8>)
public ResultCode FinishInitialize(ServiceCtx context) public ResultCode FinishInitialize(ServiceCtx context)
{ {
Logger.PrintStub(LogClass.ServiceNv); throw new ServiceNotImplementedException(context);
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);
} }
} }
} }

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.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)
{ {
Start = (ulong)position; Start = (ulong)position;
End = (ulong)size + Start; End = (ulong)size + Start;
} }
} }
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,
@ -31,18 +35,18 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
bool vaAllocated) : base(position, size) bool vaAllocated) : base(position, size)
{ {
PhysicalAddress = physicalAddress; PhysicalAddress = physicalAddress;
VaAllocated = vaAllocated; VaAllocated = vaAllocated;
} }
} }
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>();
} }
@ -135,7 +139,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
private Range BinarySearch(SortedList<long, Range> lst, long position) private Range BinarySearch(SortedList<long, Range> lst, long position)
{ {
int left = 0; int left = 0;
int right = lst.Count - 1; int right = lst.Count - 1;
while (left <= right) while (left <= right)
@ -168,7 +172,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
{ {
Range ltRg = null; Range ltRg = null;
int left = 0; int left = 0;
int right = lst.Count - 1; int right = lst.Count - 1;
while (left <= right) while (left <= right)
@ -197,4 +201,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
return ltRg; 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 Flags;
public int Kind; public int Kind;
@ -10,4 +13,4 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS
public long MappingSize; public long MappingSize;
public long Offset; 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.Common.Logging;
using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Memory;
using Ryujinx.HLE.HOS.Kernel.Process; 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 Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
_channels = new ConcurrentDictionary<KProcess, NvChannel>(); _channels = new ConcurrentDictionary<KProcess, NvChannel>();
} }
public static int ProcessIoctl(ServiceCtx context, int cmd) public static NvInternalResult ProcessIoctl(ServiceCtx context, int cmd)
{ {
switch (cmd & 0xffff) switch (cmd & 0xffff)
{ {
@ -44,14 +44,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
throw new NotImplementedException(cmd.ToString("x8")); 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 inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position; long outputPosition = context.Request.GetBufferType0x22().Position;
NvHostChannelSubmit args = MemoryHelper.Read<NvHostChannelSubmit>(context.Memory, inputPosition); 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++) 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); 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]; int[] cmdBufData = new int[cmdBuf.WordsCount];
@ -73,10 +73,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
// TODO: Relocation, waitchecks, etc. // TODO: Relocation, waitchecks, etc.
return NvResult.Success; return NvInternalResult.Success;
} }
private static int GetSyncpoint(ServiceCtx context) private static NvInternalResult GetSyncpoint(ServiceCtx context)
{ {
// TODO // TODO
long inputPosition = context.Request.GetBufferType0x21().Position; long inputPosition = context.Request.GetBufferType0x21().Position;
@ -88,10 +88,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
MemoryHelper.Write(context.Memory, outputPosition, args); 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 inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().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); 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; long inputPosition = context.Request.GetBufferType0x21().Position;
@ -115,29 +115,29 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
Logger.PrintStub(LogClass.ServiceNv); 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 inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position; long outputPosition = context.Request.GetBufferType0x22().Position;
NvHostChannelMapBuffer args = MemoryHelper.Read<NvHostChannelMapBuffer>(context.Memory, inputPosition); 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++) for (int index = 0; index < args.NumEntries; index++)
{ {
int handle = context.Memory.ReadInt32(inputPosition + 0xc + index * 8); 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) if (map == null)
{ {
Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{handle:x8}!"); Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{handle:x8}!");
return NvResult.InvalidInput; return NvInternalResult.InvalidInput;
} }
lock (map) 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; long inputPosition = context.Request.GetBufferType0x21().Position;
NvHostChannelMapBuffer args = MemoryHelper.Read<NvHostChannelMapBuffer>(context.Memory, inputPosition); 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++) for (int index = 0; index < args.NumEntries; index++)
{ {
int handle = context.Memory.ReadInt32(inputPosition + 0xc + index * 8); 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) if (map == null)
{ {
Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{handle:x8}!"); Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{handle:x8}!");
return NvResult.InvalidInput; return NvInternalResult.InvalidInput;
} }
lock (map) 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 inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position; long outputPosition = context.Request.GetBufferType0x22().Position;
Logger.PrintStub(LogClass.ServiceNv); 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 inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position; long outputPosition = context.Request.GetBufferType0x22().Position;
Logger.PrintStub(LogClass.ServiceNv); 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; long inputPosition = context.Request.GetBufferType0x21().Position;
@ -217,17 +217,17 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
Logger.PrintStub(LogClass.ServiceNv); 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 inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position; long outputPosition = context.Request.GetBufferType0x22().Position;
NvHostChannelSubmitGpfifo args = MemoryHelper.Read<NvHostChannelSubmitGpfifo>(context.Memory, inputPosition); 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++) 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); 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 inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position; long outputPosition = context.Request.GetBufferType0x22().Position;
Logger.PrintStub(LogClass.ServiceNv); 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 inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position; long outputPosition = context.Request.GetBufferType0x22().Position;
Logger.PrintStub(LogClass.ServiceNv); 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 inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position; long outputPosition = context.Request.GetBufferType0x22().Position;
Logger.PrintStub(LogClass.ServiceNv); 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; 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 GetChannel(context).Timeslice = 5200; // Timeslice high priority in micro-seconds
break; break;
default: default:
return NvResult.InvalidInput; return NvInternalResult.InvalidInput;
} }
Logger.PrintStub(LogClass.ServiceNv); Logger.PrintStub(LogClass.ServiceNv);
// TODO: disable and preempt channel when GPU scheduler will be implemented. // 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 inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position; long outputPosition = context.Request.GetBufferType0x22().Position;
Logger.PrintStub(LogClass.ServiceNv); 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 inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position; long outputPosition = context.Request.GetBufferType0x22().Position;
NvHostChannelSubmitGpfifo args = MemoryHelper.Read<NvHostChannelSubmitGpfifo>(context.Memory, inputPosition); 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++) 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); 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; long inputPosition = context.Request.GetBufferType0x21().Position;
int timeslice = context.Memory.ReadInt32(inputPosition); int timeslice = context.Memory.ReadInt32(inputPosition);
if (timeslice < 1000 || timeslice > 50000) if (timeslice < 1000 || timeslice > 50000)
{ {
return NvResult.InvalidInput; return NvInternalResult.InvalidInput;
} }
GetChannel(context).Timeslice = timeslice; // in micro-seconds 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. // 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) 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) switch (cmd & 0xffff)
{ {
@ -43,12 +43,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
throw new NotImplementedException(cmd.ToString("x8")); throw new NotImplementedException(cmd.ToString("x8"));
} }
private static int SyncptRead(ServiceCtx context) private static NvInternalResult SyncptRead(ServiceCtx context)
{ {
return SyncptReadMinOrMax(context, max: false); return SyncptReadMinOrMax(context, max: false);
} }
private static int SyncptIncr(ServiceCtx context) private static NvInternalResult SyncptIncr(ServiceCtx context)
{ {
long inputPosition = context.Request.GetBufferType0x21().Position; long inputPosition = context.Request.GetBufferType0x21().Position;
@ -56,30 +56,30 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
if ((uint)id >= NvHostSyncpt.SyncptsCount) if ((uint)id >= NvHostSyncpt.SyncptsCount)
{ {
return NvResult.InvalidInput; return NvInternalResult.InvalidInput;
} }
GetUserCtx(context).Syncpt.Increment(id); 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); return SyncptWait(context, extended: false);
} }
private static int SyncptWaitEx(ServiceCtx context) private static NvInternalResult SyncptWaitEx(ServiceCtx context)
{ {
return SyncptWait(context, extended: true); return SyncptWait(context, extended: true);
} }
private static int SyncptReadMax(ServiceCtx context) private static NvInternalResult SyncptReadMax(ServiceCtx context)
{ {
return SyncptReadMinOrMax(context, max: true); return SyncptReadMinOrMax(context, max: true);
} }
private static int GetConfig(ServiceCtx context) private static NvInternalResult GetConfig(ServiceCtx context)
{ {
if (!_isProductionMode) if (!_isProductionMode)
{ {
@ -123,23 +123,25 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
Logger.PrintDebug(LogClass.ServiceNv, $"Got setting {domain}!{name}"); 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); return EventWait(context, async: false);
} }
private static int EventWaitAsync(ServiceCtx context) private static NvInternalResult EventWaitAsync(ServiceCtx context)
{ {
return EventWait(context, async: true); return EventWait(context, async: true);
} }
private static int EventRegister(ServiceCtx context) private static NvInternalResult EventRegister(ServiceCtx context)
{ {
long inputPosition = context.Request.GetBufferType0x21().Position; long inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().Position; long outputPosition = context.Request.GetBufferType0x22().Position;
@ -148,10 +150,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
Logger.PrintStub(LogClass.ServiceNv); 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 inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().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) if ((uint)args.Id >= NvHostSyncpt.SyncptsCount)
{ {
return NvResult.InvalidInput; return NvInternalResult.InvalidInput;
} }
if (max) if (max)
@ -174,10 +176,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
MemoryHelper.Write(context.Memory, outputPosition, args); 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 inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().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) if ((uint)args.Id >= NvHostSyncpt.SyncptsCount)
{ {
return NvResult.InvalidInput; return NvInternalResult.InvalidInput;
} }
int result; NvInternalResult result;
if (syncpt.MinCompare(args.Id, args.Thresh)) if (syncpt.MinCompare(args.Id, args.Thresh))
{ {
result = NvResult.Success; result = NvInternalResult.Success;
} }
else if (args.Timeout == 0) else if (args.Timeout == 0)
{ {
result = NvResult.TryAgain; result = NvInternalResult.TryAgain;
} }
else else
{ {
@ -222,15 +224,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
{ {
waitEvent.WaitOne(); waitEvent.WaitOne();
result = NvResult.Success; result = NvInternalResult.Success;
} }
else if (waitEvent.WaitOne(timeout)) else if (waitEvent.WaitOne(timeout))
{ {
result = NvResult.Success; result = NvInternalResult.Success;
} }
else else
{ {
result = NvResult.TimedOut; result = NvInternalResult.TimedOut;
} }
} }
@ -245,7 +247,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
return result; 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 inputPosition = context.Request.GetBufferType0x21().Position;
long outputPosition = context.Request.GetBufferType0x22().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) if ((uint)args.Id >= NvHostSyncpt.SyncptsCount)
{ {
return NvResult.InvalidInput; return NvInternalResult.InvalidInput;
} }
void WriteArgs() void WriteArgs()
@ -270,7 +272,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
WriteArgs(); WriteArgs();
return NvResult.Success; return NvInternalResult.Success;
} }
if (!async) if (!async)
@ -282,12 +284,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
{ {
WriteArgs(); WriteArgs();
return NvResult.TryAgain; return NvInternalResult.TryAgain;
} }
NvHostEvent Event; NvHostEvent Event;
int result, eventIndex; NvInternalResult result;
int eventIndex;
if (async) if (async)
{ {
@ -295,7 +299,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
if ((uint)eventIndex >= NvHostCtrlUserCtx.EventsCount) if ((uint)eventIndex >= NvHostCtrlUserCtx.EventsCount)
{ {
return NvResult.InvalidInput; return NvInternalResult.InvalidInput;
} }
Event = GetUserCtx(context).Events[eventIndex]; Event = GetUserCtx(context).Events[eventIndex];
@ -325,11 +329,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
args.Value |= eventIndex; args.Value |= eventIndex;
result = NvResult.TryAgain; result = NvInternalResult.TryAgain;
} }
else else
{ {
result = NvResult.InvalidInput; result = NvInternalResult.InvalidInput;
} }
WriteArgs(); 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 Arch;
public int Impl; public int Impl;
public int Rev; public int Rev;
@ -40,4 +41,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuGpu
public long ChipName; public long ChipName;
public long GrCompbitStoreBaseHw; 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 WidthAlignPixels;
public int HeightAlignPixels; public int HeightAlignPixels;
@ -13,4 +16,4 @@
public int SubregionHeightAlignPixels; public int SubregionHeightAlignPixels;
public int SubregionCount; 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 namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
{ {
[StructLayout(LayoutKind.Sequential)]
struct NvMapAlloc struct NvMapAlloc
{ {
public int Handle; public int Handle;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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