Ryujinx/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs

523 lines
19 KiB
C#
Raw Normal View History

Add a new JIT compiler for CPU code (#693) * Start of the ARMeilleure project * Refactoring around the old IRAdapter, now renamed to PreAllocator * Optimize the LowestBitSet method * Add CLZ support and fix CLS implementation * Add missing Equals and GetHashCode overrides on some structs, misc small tweaks * Implement the ByteSwap IR instruction, and some refactoring on the assembler * Implement the DivideUI IR instruction and fix 64-bits IDIV * Correct constant operand type on CSINC * Move division instructions implementation to InstEmitDiv * Fix destination type for the ConditionalSelect IR instruction * Implement UMULH and SMULH, with new IR instructions * Fix some issues with shift instructions * Fix constant types for BFM instructions * Fix up new tests using the new V128 struct * Update tests * Move DIV tests to a separate file * Add support for calls, and some instructions that depends on them * Start adding support for SIMD & FP types, along with some of the related ARM instructions * Fix some typos and the divide instruction with FP operands * Fix wrong method call on Clz_V * Implement ARM FP & SIMD move instructions, Saddlv_V, and misc. fixes * Implement SIMD logical instructions and more misc. fixes * Fix PSRAD x86 instruction encoding, TRN, UABD and UABDL implementations * Implement float conversion instruction, merge in LDj3SNuD fixes, and some other misc. fixes * Implement SIMD shift instruction and fix Dup_V * Add SCVTF and UCVTF (vector, fixed-point) variants to the opcode table * Fix check with tolerance on tester * Implement FP & SIMD comparison instructions, and some fixes * Update FCVT (Scalar) encoding on the table to support the Half-float variants * Support passing V128 structs, some cleanup on the register allocator, merge LDj3SNuD fixes * Use old memory access methods, made a start on SIMD memory insts support, some fixes * Fix float constant passed to functions, save and restore non-volatile XMM registers, other fixes * Fix arguments count with struct return values, other fixes * More instructions * Misc. fixes and integrate LDj3SNuD fixes * Update tests * Add a faster linear scan allocator, unwinding support on windows, and other changes * Update Ryujinx.HLE * Update Ryujinx.Graphics * Fix V128 return pointer passing, RCX is clobbered * Update Ryujinx.Tests * Update ITimeZoneService * Stop using GetFunctionPointer as that can't be called from native code, misc. fixes and tweaks * Use generic GetFunctionPointerForDelegate method and other tweaks * Some refactoring on the code generator, assert on invalid operations and use a separate enum for intrinsics * Remove some unused code on the assembler * Fix REX.W prefix regression on float conversion instructions, add some sort of profiler * Add hardware capability detection * Fix regression on Sha1h and revert Fcm** changes * Add SSE2-only paths on vector extract and insert, some refactoring on the pre-allocator * Fix silly mistake introduced on last commit on CpuId * Generate inline stack probes when the stack allocation is too large * Initial support for the System-V ABI * Support multiple destination operands * Fix SSE2 VectorInsert8 path, and other fixes * Change placement of XMM callee save and restore code to match other compilers * Rename Dest to Destination and Inst to Instruction * Fix a regression related to calls and the V128 type * Add an extra space on comments to match code style * Some refactoring * Fix vector insert FP32 SSE2 path * Port over the ARM32 instructions * Avoid memory protection races on JIT Cache * Another fix on VectorInsert FP32 (thanks to LDj3SNuD * Float operands don't need to use the same register when VEX is supported * Add a new register allocator, higher quality code for hot code (tier up), and other tweaks * Some nits, small improvements on the pre allocator * CpuThreadState is gone * Allow changing CPU emulators with a config entry * Add runtime identifiers on the ARMeilleure project * Allow switching between CPUs through a config entry (pt. 2) * Change win10-x64 to win-x64 on projects * Update the Ryujinx project to use ARMeilleure * Ensure that the selected register is valid on the hybrid allocator * Allow exiting on returns to 0 (should fix test regression) * Remove register assignments for most used variables on the hybrid allocator * Do not use fixed registers as spill temp * Add missing namespace and remove unneeded using * Address PR feedback * Fix types, etc * Enable AssumeStrictAbiCompliance by default * Ensure that Spill and Fill don't load or store any more than necessary
2019-08-08 18:56:22 +00:00
using ARMeilleure.Memory;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
using Ryujinx.HLE.HOS.Services.Nv.Types;
2018-02-04 23:08:20 +00:00
using System;
using System.Collections.Generic;
using System.Reflection;
2018-02-04 23:08:20 +00:00
namespace Ryujinx.HLE.HOS.Services.Nv
2018-02-04 23:08:20 +00:00
{
[Service("nvdrv")]
[Service("nvdrv:a")]
[Service("nvdrv:s")]
[Service("nvdrv:t")]
class INvDrvServices : IpcService
2018-02-04 23:08:20 +00:00
{
private static Dictionary<string, Type> _deviceFileRegistry =
new Dictionary<string, Type>()
{
{ "/dev/nvmap", typeof(NvMapDeviceFile) },
{ "/dev/nvhost-ctrl", typeof(NvHostCtrlDeviceFile) },
{ "/dev/nvhost-ctrl-gpu", typeof(NvHostCtrlGpuDeviceFile) },
{ "/dev/nvhost-as-gpu", typeof(NvHostAsGpuDeviceFile) },
{ "/dev/nvhost-gpu", typeof(NvHostGpuDeviceFile) },
//{ "/dev/nvhost-msenc", typeof(NvHostChannelDeviceFile) },
{ "/dev/nvhost-nvdec", typeof(NvHostChannelDeviceFile) },
//{ "/dev/nvhost-nvjpg", typeof(NvHostChannelDeviceFile) },
{ "/dev/nvhost-vic", typeof(NvHostChannelDeviceFile) },
//{ "/dev/nvhost-display", typeof(NvHostChannelDeviceFile) },
};
private static IdDictionary _deviceFileIdRegistry = new IdDictionary();
private KProcess _owner;
public INvDrvServices(ServiceCtx context)
{
_owner = null;
}
private int Open(ServiceCtx context, string path)
{
if (context.Process == _owner)
{
if (_deviceFileRegistry.TryGetValue(path, out Type deviceFileClass))
{
ConstructorInfo constructor = deviceFileClass.GetConstructor(new Type[] { typeof(ServiceCtx) });
NvDeviceFile deviceFile = (NvDeviceFile)constructor.Invoke(new object[] { context });
return _deviceFileIdRegistry.Add(deviceFile);
}
else
{
Logger.PrintWarning(LogClass.ServiceNv, $"Cannot find file device \"{path}\"!");
}
}
return -1;
}
private NvResult GetIoctlArgument(ServiceCtx context, NvIoctl ioctlCommand, out Span<byte> arguments)
{
(long inputDataPosition, long inputDataSize) = context.Request.GetBufferType0x21(0);
(long outputDataPosition, long outputDataSize) = context.Request.GetBufferType0x22(0);
NvIoctl.Direction ioctlDirection = ioctlCommand.DirectionValue;
uint ioctlSize = ioctlCommand.Size;
bool isRead = (ioctlDirection & NvIoctl.Direction.Read) != 0;
bool isWrite = (ioctlDirection & NvIoctl.Direction.Write) != 0;
if ((isWrite && ioctlSize > outputDataSize) || (isRead && ioctlSize > inputDataSize))
{
arguments = null;
Logger.PrintWarning(LogClass.ServiceNv, "Ioctl size inconsistency found!");
return NvResult.InvalidSize;
}
if (isRead && isWrite)
{
if (outputDataSize < inputDataSize)
{
arguments = null;
Logger.PrintWarning(LogClass.ServiceNv, "Ioctl size inconsistency found!");
return NvResult.InvalidSize;
}
byte[] outputData = new byte[outputDataSize];
2019-10-13 06:02:07 +00:00
byte[] temp = context.Memory.ReadBytes(inputDataPosition, inputDataSize);
Buffer.BlockCopy(temp, 0, outputData, 0, temp.Length);
arguments = new Span<byte>(outputData);
}
else if (isWrite)
{
byte[] outputData = new byte[outputDataSize];
arguments = new Span<byte>(outputData);
}
else
{
arguments = new Span<byte>(context.Memory.ReadBytes(inputDataPosition, inputDataSize));
}
return NvResult.Success;
}
private NvResult GetDeviceFileFromFd(int fd, out NvDeviceFile deviceFile)
{
deviceFile = null;
if (fd < 0)
{
return NvResult.InvalidParameter;
}
deviceFile = _deviceFileIdRegistry.GetData<NvDeviceFile>(fd);
if (deviceFile == null)
{
Logger.PrintWarning(LogClass.ServiceNv, $"Invalid file descriptor {fd}");
return NvResult.NotImplemented;
}
if (deviceFile.Owner.Pid != _owner.Pid)
{
return NvResult.AccessDenied;
}
return NvResult.Success;
}
private NvResult EnsureInitialized()
{
if (_owner == null)
{
Logger.PrintWarning(LogClass.ServiceNv, "INvDrvServices is not initialized!");
return NvResult.NotInitialized;
}
return NvResult.Success;
}
private static NvResult ConvertInternalErrorCode(NvInternalResult errorCode)
{
switch (errorCode)
{
case NvInternalResult.Success:
return NvResult.Success;
case NvInternalResult.Unknown0x72:
return NvResult.AlreadyAllocated;
case NvInternalResult.TimedOut:
case NvInternalResult.TryAgain:
case NvInternalResult.Interrupted:
return NvResult.Timeout;
case NvInternalResult.InvalidAddress:
return NvResult.InvalidAddress;
case NvInternalResult.NotSupported:
case NvInternalResult.Unknown0x18:
return NvResult.NotSupported;
case NvInternalResult.InvalidState:
return NvResult.InvalidState;
case NvInternalResult.ReadOnlyAttribute:
return NvResult.ReadOnlyAttribute;
case NvInternalResult.NoSpaceLeft:
case NvInternalResult.FileTooBig:
return NvResult.InvalidSize;
case NvInternalResult.FileTableOverflow:
case NvInternalResult.BadFileNumber:
return NvResult.FileOperationFailed;
case NvInternalResult.InvalidInput:
return NvResult.InvalidValue;
case NvInternalResult.NotADirectory:
return NvResult.DirectoryOperationFailed;
case NvInternalResult.Busy:
return NvResult.Busy;
case NvInternalResult.BadAddress:
return NvResult.InvalidAddress;
case NvInternalResult.AccessDenied:
case NvInternalResult.OperationNotPermitted:
return NvResult.AccessDenied;
case NvInternalResult.OutOfMemory:
return NvResult.InsufficientMemory;
case NvInternalResult.DeviceNotFound:
return NvResult.ModuleNotPresent;
case NvInternalResult.IoError:
return NvResult.ResourceError;
default:
return NvResult.IoctlFailed;
}
}
[Command(0)]
// Open(buffer<bytes, 5> path) -> (s32 fd, u32 error_code)
public ResultCode Open(ServiceCtx context)
2018-02-04 23:08:20 +00:00
{
NvResult errorCode = EnsureInitialized();
int fd = -1;
if (errorCode == NvResult.Success)
{
long pathPtr = context.Request.SendBuff[0].Position;
2018-02-04 23:08:20 +00:00
string path = MemoryHelper.ReadAsciiString(context.Memory, pathPtr);
2018-02-04 23:08:20 +00:00
fd = Open(context, path);
if (fd == -1)
{
errorCode = NvResult.FileOperationFailed;
}
}
2018-02-04 23:08:20 +00:00
context.ResponseData.Write(fd);
context.ResponseData.Write((uint)errorCode);
2018-02-04 23:08:20 +00:00
return ResultCode.Success;
2018-02-04 23:08:20 +00:00
}
[Command(1)]
// Ioctl(s32 fd, u32 ioctl_cmd, buffer<bytes, 0x21> in_args) -> (u32 error_code, buffer<bytes, 0x22> out_args)
public ResultCode Ioctl(ServiceCtx context)
2018-02-04 23:08:20 +00:00
{
NvResult errorCode = EnsureInitialized();
if (errorCode == NvResult.Success)
{
int fd = context.RequestData.ReadInt32();
NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>();
2018-02-04 23:08:20 +00:00
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
2018-02-04 23:08:20 +00:00
if (errorCode == NvResult.Success)
{
errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile);
if (errorCode == NvResult.Success)
{
NvInternalResult internalResult = deviceFile.Ioctl(ioctlCommand, arguments);
if (internalResult == NvInternalResult.NotImplemented)
{
throw new NvIoctlNotImplementedException(context, deviceFile, ioctlCommand);
}
errorCode = ConvertInternalErrorCode(internalResult);
if (errorCode == NvResult.Success && (ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
{
context.Memory.WriteBytes(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
}
}
}
2018-02-04 23:08:20 +00:00
}
context.ResponseData.Write((uint)errorCode);
return ResultCode.Success;
2018-02-04 23:08:20 +00:00
}
[Command(2)]
// Close(s32 fd) -> u32 error_code
public ResultCode Close(ServiceCtx context)
2018-02-04 23:08:20 +00:00
{
NvResult errorCode = EnsureInitialized();
2018-02-04 23:08:20 +00:00
if (errorCode == NvResult.Success)
{
int fd = context.RequestData.ReadInt32();
2018-02-04 23:08:20 +00:00
errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile);
if (errorCode == NvResult.Success)
{
deviceFile.Close();
_deviceFileIdRegistry.Delete(fd);
}
}
context.ResponseData.Write((uint)errorCode);
2018-02-04 23:08:20 +00:00
return ResultCode.Success;
2018-02-04 23:08:20 +00:00
}
[Command(3)]
// Initialize(u32 transfer_memory_size, handle<copy, process> current_process, handle<copy, transfer_memory> transfer_memory) -> u32 error_code
public ResultCode Initialize(ServiceCtx context)
2018-02-04 23:08:20 +00:00
{
long transferMemSize = context.RequestData.ReadInt64();
int transferMemHandle = context.Request.HandleDesc.ToCopy[0];
2018-02-04 23:08:20 +00:00
_owner = context.Process;
2018-02-04 23:08:20 +00:00
context.ResponseData.Write((uint)NvResult.Success);
return ResultCode.Success;
2018-02-04 23:08:20 +00:00
}
[Command(4)]
// QueryEvent(s32 fd, u32 event_id) -> (u32, handle<copy, event>)
public ResultCode QueryEvent(ServiceCtx context)
2018-02-04 23:08:20 +00:00
{
NvResult errorCode = EnsureInitialized();
2018-02-04 23:08:20 +00:00
if (errorCode == NvResult.Success)
{
int fd = context.RequestData.ReadInt32();
uint eventId = context.RequestData.ReadUInt32();
errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile);
if (errorCode == NvResult.Success)
{
NvInternalResult internalResult = deviceFile.QueryEvent(out int eventHandle, eventId);
if (internalResult == NvInternalResult.NotImplemented)
{
throw new NvQueryEventNotImplementedException(context, deviceFile, eventId);
}
errorCode = ConvertInternalErrorCode(internalResult);
if (errorCode == NvResult.Success)
{
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(eventHandle);
}
}
}
context.ResponseData.Write((uint)errorCode);
2018-02-04 23:08:20 +00:00
return ResultCode.Success;
}
[Command(5)]
// MapSharedMemory(s32 fd, u32 argument, handle<copy, shared_memory>) -> u32 error_code
public ResultCode MapSharedMemory(ServiceCtx context)
{
NvResult errorCode = EnsureInitialized();
if (errorCode == NvResult.Success)
{
int fd = context.RequestData.ReadInt32();
uint argument = context.RequestData.ReadUInt32();
int sharedMemoryHandle = context.Request.HandleDesc.ToCopy[0];
errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile);
if (errorCode == NvResult.Success)
{
KSharedMemory sharedMemory = context.Process.HandleTable.GetObject<KSharedMemory>(sharedMemoryHandle);
errorCode = ConvertInternalErrorCode(deviceFile.MapSharedMemory(sharedMemory, argument));
}
}
context.ResponseData.Write((uint)errorCode);
2018-02-04 23:08:20 +00:00
return ResultCode.Success;
2018-02-04 23:08:20 +00:00
}
[Command(6)]
// GetStatus() -> (unknown<0x20>, u32 error_code)
public ResultCode GetStatus(ServiceCtx context)
{
throw new ServiceNotImplementedException(context);
}
[Command(7)]
// ForceSetClientPid(u64) -> u32 error_code
public ResultCode ForceSetClientPid(ServiceCtx context)
{
throw new ServiceNotImplementedException(context);
}
[Command(8)]
// SetClientPID(u64, pid) -> u32 error_code
public ResultCode SetClientPid(ServiceCtx context)
{
long pid = context.RequestData.ReadInt64();
context.ResponseData.Write(0);
return ResultCode.Success;
}
[Command(9)]
// DumpGraphicsMemoryInfo()
public ResultCode DumpGraphicsMemoryInfo(ServiceCtx context)
{
Logger.PrintStub(LogClass.ServiceNv);
return ResultCode.Success;
}
[Command(10)] // 3.0.0+
// InitializeDevtools(u32, handle<copy>) -> u32 error_code;
public ResultCode InitializeDevtools(ServiceCtx context)
{
throw new ServiceNotImplementedException(context);
}
[Command(11)] // 3.0.0+
// Ioctl2(s32 fd, u32 ioctl_cmd, buffer<bytes, 0x21> in_args, buffer<bytes, 0x21> inline_in_buffer) -> (u32 error_code, buffer<bytes, 0x22> out_args)
public ResultCode Ioctl2(ServiceCtx context)
2018-02-04 23:08:20 +00:00
{
NvResult errorCode = EnsureInitialized();
2018-02-04 23:08:20 +00:00
if (errorCode == NvResult.Success)
{
int fd = context.RequestData.ReadInt32();
NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>();
2018-02-04 23:08:20 +00:00
(long inlineInBufferPosition, long inlineInBufferSize) = context.Request.GetBufferType0x21(1);
2018-02-04 23:08:20 +00:00
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
2018-02-04 23:08:20 +00:00
Span<byte> inlineInBuffer = new Span<byte>(context.Memory.ReadBytes(inlineInBufferPosition, inlineInBufferSize));
2018-02-04 23:08:20 +00:00
if (errorCode == NvResult.Success)
{
errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile);
if (errorCode == NvResult.Success)
{
NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBuffer);
2018-02-04 23:08:20 +00:00
if (internalResult == NvInternalResult.NotImplemented)
{
throw new NvIoctlNotImplementedException(context, deviceFile, ioctlCommand);
}
errorCode = ConvertInternalErrorCode(internalResult);
if (errorCode == NvResult.Success && (ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
{
context.Memory.WriteBytes(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
}
}
}
2018-02-04 23:08:20 +00:00
}
context.ResponseData.Write((uint)errorCode);
2018-02-04 23:08:20 +00:00
return ResultCode.Success;
}
[Command(12)] // 3.0.0+
// Ioctl3(s32 fd, u32 ioctl_cmd, buffer<bytes, 0x21> in_args) -> (u32 error_code, buffer<bytes, 0x22> out_args, buffer<bytes, 0x22> inline_out_buffer)
public ResultCode Ioctl3(ServiceCtx context)
2018-02-04 23:08:20 +00:00
{
NvResult errorCode = EnsureInitialized();
2018-02-04 23:08:20 +00:00
if (errorCode == NvResult.Success)
{
int fd = context.RequestData.ReadInt32();
NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>();
(long inlineOutBufferPosition, long inlineOutBufferSize) = context.Request.GetBufferType0x22(1);
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
Span<byte> inlineOutBuffer = new Span<byte>(context.Memory.ReadBytes(inlineOutBufferPosition, inlineOutBufferSize));
if (errorCode == NvResult.Success)
{
errorCode = GetDeviceFileFromFd(fd, out NvDeviceFile deviceFile);
if (errorCode == NvResult.Success)
{
NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBuffer);
2018-02-04 23:08:20 +00:00
if (internalResult == NvInternalResult.NotImplemented)
{
throw new NvIoctlNotImplementedException(context, deviceFile, ioctlCommand);
}
errorCode = ConvertInternalErrorCode(internalResult);
2018-02-04 23:08:20 +00:00
if (errorCode == NvResult.Success && (ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
{
context.Memory.WriteBytes(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
context.Memory.WriteBytes(inlineOutBufferPosition, inlineOutBuffer.ToArray());
}
}
}
}
context.ResponseData.Write((uint)errorCode);
return ResultCode.Success;
}
[Command(13)] // 3.0.0+
// FinishInitialize(unknown<8>)
public ResultCode FinishInitialize(ServiceCtx context)
{
Logger.PrintStub(LogClass.ServiceNv);
return ResultCode.Success;
2018-02-04 23:08:20 +00:00
}
}
}