aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs')
-rw-r--r--Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs243
1 files changed, 243 insertions, 0 deletions
diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
new file mode 100644
index 00000000..c96c04c8
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
@@ -0,0 +1,243 @@
+using ChocolArm64.Memory;
+using Ryujinx.HLE.Gpu;
+using Ryujinx.HLE.Logging;
+using Ryujinx.HLE.OsHle.Services.Nv.NvMap;
+using System;
+using System.Collections.Concurrent;
+
+namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
+{
+ class NvGpuASIoctl
+ {
+ private const int FlagFixedOffset = 1;
+
+ private static ConcurrentDictionary<Process, NvGpuVmm> Vmms;
+
+ static NvGpuASIoctl()
+ {
+ Vmms = new ConcurrentDictionary<Process, NvGpuVmm>();
+ }
+
+ 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);
+ }
+
+ throw new NotImplementedException(Cmd.ToString("x8"));
+ }
+
+ private static int BindChannel(ServiceCtx Context)
+ {
+ long InputPosition = Context.Request.GetBufferType0x21().Position;
+ long OutputPosition = Context.Request.GetBufferType0x22().Position;
+
+ Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed.");
+
+ return NvResult.Success;
+ }
+
+ private static int AllocSpace(ServiceCtx Context)
+ {
+ long InputPosition = Context.Request.GetBufferType0x21().Position;
+ long OutputPosition = Context.Request.GetBufferType0x22().Position;
+
+ NvGpuASAllocSpace Args = AMemoryHelper.Read<NvGpuASAllocSpace>(Context.Memory, InputPosition);
+
+ NvGpuVmm Vmm = GetVmm(Context);
+
+ ulong Size = (ulong)Args.Pages *
+ (ulong)Args.PageSize;
+
+ if ((Args.Flags & FlagFixedOffset) != 0)
+ {
+ Args.Offset = Vmm.Reserve(Args.Offset, (long)Size, 1);
+ }
+ else
+ {
+ Args.Offset = Vmm.Reserve((long)Size, 1);
+ }
+
+ int Result = NvResult.Success;
+
+ if (Args.Offset < 0)
+ {
+ Args.Offset = 0;
+
+ Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"No memory to allocate size {Size:x16}!");
+
+ Result = NvResult.OutOfMemory;
+ }
+
+ AMemoryHelper.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 = AMemoryHelper.Read<NvGpuASAllocSpace>(Context.Memory, InputPosition);
+
+ NvGpuVmm Vmm = GetVmm(Context);
+
+ ulong Size = (ulong)Args.Pages *
+ (ulong)Args.PageSize;
+
+ Vmm.Free(Args.Offset, (long)Size);
+
+ return NvResult.Success;
+ }
+
+ private static int UnmapBuffer(ServiceCtx Context)
+ {
+ long InputPosition = Context.Request.GetBufferType0x21().Position;
+ long OutputPosition = Context.Request.GetBufferType0x22().Position;
+
+ NvGpuASUnmapBuffer Args = AMemoryHelper.Read<NvGpuASUnmapBuffer>(Context.Memory, InputPosition);
+
+ NvGpuVmm Vmm = GetVmm(Context);
+
+ if (!Vmm.Unmap(Args.Offset))
+ {
+ Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {Args.Offset:x16}!");
+ }
+
+ return NvResult.Success;
+ }
+
+ private static int MapBufferEx(ServiceCtx Context)
+ {
+ long InputPosition = Context.Request.GetBufferType0x21().Position;
+ long OutputPosition = Context.Request.GetBufferType0x22().Position;
+
+ NvGpuASMapBufferEx Args = AMemoryHelper.Read<NvGpuASMapBufferEx>(Context.Memory, InputPosition);
+
+ NvGpuVmm Vmm = GetVmm(Context);
+
+ NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle);
+
+ if (Map == null)
+ {
+ Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!");
+
+ return NvResult.InvalidInput;
+ }
+
+ long PA = Map.Address + Args.BufferOffset;
+
+ long Size = Args.MappingSize;
+
+ if (Size == 0)
+ {
+ Size = (uint)Map.Size;
+ }
+
+ int Result = NvResult.Success;
+
+ //Note: When the fixed offset flag is not set,
+ //the Offset field holds the alignment size instead.
+ if ((Args.Flags & FlagFixedOffset) != 0)
+ {
+ long MapEnd = Args.Offset + Args.MappingSize;
+
+ if ((ulong)MapEnd <= (ulong)Args.Offset)
+ {
+ Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Offset 0x{Args.Offset:x16} and size 0x{Args.MappingSize:x16} results in a overflow!");
+
+ return NvResult.InvalidInput;
+ }
+
+ if ((Args.Offset & NvGpuVmm.PageMask) != 0)
+ {
+ Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Offset 0x{Args.Offset:x16} is not page aligned!");
+
+ return NvResult.InvalidInput;
+ }
+
+ Args.Offset = Vmm.Map(PA, Args.Offset, Size);
+ }
+ else
+ {
+ Args.Offset = Vmm.Map(PA, Size);
+
+ if (Args.Offset < 0)
+ {
+ Args.Offset = 0;
+
+ Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"No memory to map size {Args.MappingSize:x16}!");
+
+ Result = NvResult.InvalidInput;
+ }
+ }
+
+ AMemoryHelper.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;
+
+ Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed.");
+
+ return NvResult.Success;
+ }
+
+ private static int InitializeEx(ServiceCtx Context)
+ {
+ long InputPosition = Context.Request.GetBufferType0x21().Position;
+ long OutputPosition = Context.Request.GetBufferType0x22().Position;
+
+ Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed.");
+
+ return NvResult.Success;
+ }
+
+ private static int Remap(ServiceCtx Context)
+ {
+ long InputPosition = Context.Request.GetBufferType0x21().Position;
+
+ NvGpuASRemap Args = AMemoryHelper.Read<NvGpuASRemap>(Context.Memory, InputPosition);
+
+ NvGpuVmm Vmm = GetVmm(Context);
+
+ NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle);
+
+ if (Map == null)
+ {
+ Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!");
+
+ return NvResult.InvalidInput;
+ }
+
+ //FIXME: This is most likely wrong...
+ Vmm.Map(Map.Address, (long)(uint)Args.Offset << 16,
+ (long)(uint)Args.Pages << 16);
+
+ return NvResult.Success;
+ }
+
+ public static NvGpuVmm GetVmm(ServiceCtx Context)
+ {
+ return Vmms.GetOrAdd(Context.Process, (Key) => new NvGpuVmm(Context.Memory));
+ }
+
+ public static void UnloadProcess(Process Process)
+ {
+ Vmms.TryRemove(Process, out _);
+ }
+ }
+} \ No newline at end of file