diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2018-05-07 15:53:23 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-05-07 15:53:23 -0300 |
| commit | 34037701c708cb70bbf44dea71ee0912f7b4102b (patch) | |
| tree | ca4cf2bde85dea48af12033b8d0446f17b611f4f /Ryujinx.Core/OsHle | |
| parent | 4419e8d6b43432eae94a3a9304f7df22b34738a8 (diff) | |
NvServices refactoring (#120)
* Initial implementation of NvMap/NvHostCtrl
* More work on NvHostCtrl
* Refactoring of nvservices, move GPU Vmm, make Vmm per-process, refactor most gpu devices, move Gpu to Core, fix CbBind
* Implement GetGpuTime, support CancelSynchronization, fix issue on InsertWaitingMutex, proper double buffering support (again, not working properly for commercial games, only hb)
* Try to fix perf regression reading/writing textures, moved syncpts and events to a UserCtx class, delete global state when the process exits, other minor tweaks
* Remove now unused code, add comment about probably wrong result codes
Diffstat (limited to 'Ryujinx.Core/OsHle')
45 files changed, 1779 insertions, 929 deletions
diff --git a/Ryujinx.Core/OsHle/Horizon.cs b/Ryujinx.Core/OsHle/Horizon.cs index 0cc3d815..1d4098c7 100644 --- a/Ryujinx.Core/OsHle/Horizon.cs +++ b/Ryujinx.Core/OsHle/Horizon.cs @@ -20,6 +20,8 @@ namespace Ryujinx.Core.OsHle public SystemStateMgr SystemState { get; private set; } + internal MemoryAllocator Allocator { get; private set; } + internal HSharedMem HidSharedMem { get; private set; } internal HSharedMem FontSharedMem { get; private set; } @@ -35,6 +37,8 @@ namespace Ryujinx.Core.OsHle SystemState = new SystemStateMgr(); + Allocator = new MemoryAllocator(); + HidSharedMem = new HSharedMem(); FontSharedMem = new HSharedMem(); diff --git a/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs b/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs index 42322d41..420890eb 100644 --- a/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Core.OsHle.Ipc { static class IpcHandler { - public static void IpcCall( + public static long IpcCall( Switch Ns, Process Process, AMemory Memory, @@ -94,6 +94,8 @@ namespace Ryujinx.Core.OsHle.Ipc AMemoryHelper.WriteBytes(Memory, CmdPtr, Response.GetBytes(CmdPtr)); } + + return 0; } private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values) diff --git a/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs b/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs index d81f44bd..afcbb3a4 100644 --- a/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs @@ -174,31 +174,6 @@ namespace Ryujinx.Core.OsHle.Ipc return 0; } - public long GetSendBuffPtr() - { - if (SendBuff.Count > 0 && SendBuff[0].Size != 0) - { - return SendBuff[0].Position; - } - - if (PtrBuff.Count > 0 && PtrBuff[0].Size != 0) - { - return PtrBuff[0].Position; - } - - if (ReceiveBuff.Count > 0 && ReceiveBuff[0].Size != 0) - { - return ReceiveBuff[0].Position; - } - - if (RecvListBuff.Count > 0 && RecvListBuff[0].Size != 0) - { - return RecvListBuff[0].Position; - } - - return -1; - } - public long GetBufferType0x21Position() { if (PtrBuff.Count > 0 && PtrBuff[0].Position != 0) diff --git a/Ryujinx.Core/OsHle/Kernel/KernelErr.cs b/Ryujinx.Core/OsHle/Kernel/KernelErr.cs index b568405b..87f9cf3b 100644 --- a/Ryujinx.Core/OsHle/Kernel/KernelErr.cs +++ b/Ryujinx.Core/OsHle/Kernel/KernelErr.cs @@ -7,6 +7,8 @@ namespace Ryujinx.Core.OsHle.Kernel public const int InvalidMemRange = 110; public const int InvalidHandle = 114; public const int Timeout = 117; + public const int Canceled = 118; + public const int CountOutOfRange = 119; public const int InvalidInfo = 120; } }
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs index e855b77d..1874360b 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs @@ -5,6 +5,8 @@ using Ryujinx.Core.Logging; using Ryujinx.Core.OsHle.Handles; using System; using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Threading; namespace Ryujinx.Core.OsHle.Kernel { @@ -18,12 +20,16 @@ namespace Ryujinx.Core.OsHle.Kernel private Process Process; private AMemory Memory; + private ConcurrentDictionary<KThread, AutoResetEvent> SyncWaits; + private object CondVarLock; private HashSet<(HSharedMem, long)> MappedSharedMems; private ulong CurrentHeapSize; + private const uint SelfHandle = 0xffff8001; + private static Random Rng; public SvcHandler(Switch Ns, Process Process) @@ -51,6 +57,7 @@ namespace Ryujinx.Core.OsHle.Kernel { 0x16, SvcCloseHandle }, { 0x17, SvcResetSignal }, { 0x18, SvcWaitSynchronization }, + { 0x19, SvcCancelSynchronization }, { 0x1a, SvcArbitrateLock }, { 0x1b, SvcArbitrateUnlock }, { 0x1c, SvcWaitProcessWideKeyAtomic }, @@ -70,6 +77,8 @@ namespace Ryujinx.Core.OsHle.Kernel this.Process = Process; this.Memory = Process.Memory; + SyncWaits = new ConcurrentDictionary<KThread, AutoResetEvent>(); + CondVarLock = new object(); MappedSharedMems = new HashSet<(HSharedMem, long)>(); @@ -100,6 +109,18 @@ namespace Ryujinx.Core.OsHle.Kernel } } + private KThread GetThread(long Tpidr, int Handle) + { + if ((uint)Handle == SelfHandle) + { + return Process.GetThread(Tpidr); + } + else + { + return Process.HandleTable.GetData<KThread>(Handle); + } + } + public void Dispose() { Dispose(true); diff --git a/Ryujinx.Core/OsHle/Kernel/SvcSystem.cs b/Ryujinx.Core/OsHle/Kernel/SvcSystem.cs index 601b211c..e5b080a8 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcSystem.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcSystem.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Core.OsHle.Kernel if (Obj == null) { - Ns.Log.PrintWarning(LogClass.KernelSvc, $"Tried to CloseHandle on invalid handle 0x{Handle:x8}!"); + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -88,9 +88,21 @@ namespace Ryujinx.Core.OsHle.Kernel int HandlesCount = (int)ThreadState.X2; ulong Timeout = ThreadState.X3; + Ns.Log.PrintDebug(LogClass.KernelSvc, + "HandlesPtr = " + HandlesPtr .ToString("x16") + ", " + + "HandlesCount = " + HandlesCount.ToString("x8") + ", " + + "Timeout = " + Timeout .ToString("x16")); + + if ((uint)HandlesCount > 0x40) + { + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.CountOutOfRange); + + return; + } + KThread CurrThread = Process.GetThread(ThreadState.Tpidr); - WaitHandle[] Handles = new WaitHandle[HandlesCount]; + WaitHandle[] Handles = new WaitHandle[HandlesCount + 1]; for (int Index = 0; Index < HandlesCount; Index++) { @@ -110,34 +122,73 @@ namespace Ryujinx.Core.OsHle.Kernel Handles[Index] = SyncObj.WaitEvent; } - Process.Scheduler.Suspend(CurrThread.ProcessorId); + using (AutoResetEvent WaitEvent = new AutoResetEvent(false)) + { + if (!SyncWaits.TryAdd(CurrThread, WaitEvent)) + { + throw new InvalidOperationException(); + } + + Handles[HandlesCount] = WaitEvent; - int HandleIndex; + Process.Scheduler.Suspend(CurrThread.ProcessorId); - ulong Result = 0; + int HandleIndex; - if (Timeout != ulong.MaxValue) - { - HandleIndex = WaitHandle.WaitAny(Handles, NsTimeConverter.GetTimeMs(Timeout)); + ulong Result = 0; + + if (Timeout != ulong.MaxValue) + { + HandleIndex = WaitHandle.WaitAny(Handles, NsTimeConverter.GetTimeMs(Timeout)); + } + else + { + HandleIndex = WaitHandle.WaitAny(Handles); + } if (HandleIndex == WaitHandle.WaitTimeout) { Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout); } + else if (HandleIndex == HandlesCount) + { + Result = MakeError(ErrorModule.Kernel, KernelErr.Canceled); + } + + SyncWaits.TryRemove(CurrThread, out _); + + Process.Scheduler.Resume(CurrThread); + + ThreadState.X0 = Result; + + if (Result == 0) + { + ThreadState.X1 = (ulong)HandleIndex; + } } - else + } + + private void SvcCancelSynchronization(AThreadState ThreadState) + { + int ThreadHandle = (int)ThreadState.X0; + + KThread Thread = GetThread(ThreadState.Tpidr, ThreadHandle); + + if (Thread == null) { - HandleIndex = WaitHandle.WaitAny(Handles); - } + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!"); - Process.Scheduler.Resume(CurrThread); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); - ThreadState.X0 = Result; + return; + } - if (Result == 0) + if (SyncWaits.TryRemove(Thread, out AutoResetEvent WaitEvent)) { - ThreadState.X1 = (ulong)HandleIndex; + WaitEvent.Set(); } + + ThreadState.X0 = 0; } private void SvcGetSystemTick(AThreadState ThreadState) @@ -190,13 +241,13 @@ namespace Ryujinx.Core.OsHle.Kernel IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr); - IpcHandler.IpcCall(Ns, Process, Memory, Session, Cmd, CmdPtr); + long Result = IpcHandler.IpcCall(Ns, Process, Memory, Session, Cmd, CmdPtr); Thread.Yield(); Process.Scheduler.Resume(CurrThread); - ThreadState.X0 = 0; + ThreadState.X0 = (ulong)Result; } else { diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs b/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs index e382cf75..57608cda 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs @@ -191,6 +191,8 @@ namespace Ryujinx.Core.OsHle.Kernel InsertWaitingMutexThread(OwnerThreadHandle, WaitThread); + Ns.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state..."); + Process.Scheduler.EnterWait(CurrThread); } @@ -297,6 +299,8 @@ namespace Ryujinx.Core.OsHle.Kernel } } + Ns.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state..."); + if (Timeout != ulong.MaxValue) { return Process.Scheduler.EnterWait(WaitThread, NsTimeConverter.GetTimeMs(Timeout)); @@ -407,7 +411,7 @@ namespace Ryujinx.Core.OsHle.Kernel if (CurrThread != WaitThread) { - if (WaitThread.NextCondVarThread != null) + if (WaitThread.NextMutexThread != null) { throw new InvalidOperationException(); } diff --git a/Ryujinx.Core/OsHle/MemoryAllocator.cs b/Ryujinx.Core/OsHle/MemoryAllocator.cs new file mode 100644 index 00000000..e57f5264 --- /dev/null +++ b/Ryujinx.Core/OsHle/MemoryAllocator.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ryujinx.Core.OsHle +{ + class MemoryAllocator + { + public bool TryAllocate(long Size, out long Address) + { + throw new NotImplementedException(); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Process.cs b/Ryujinx.Core/OsHle/Process.cs index b8c08856..3ccbc016 100644 --- a/Ryujinx.Core/OsHle/Process.cs +++ b/Ryujinx.Core/OsHle/Process.cs @@ -410,11 +410,7 @@ namespace Ryujinx.Core.OsHle } } - INvDrvServices.Fds.DeleteProcess(this); - - INvDrvServices.NvMaps .DeleteProcess(this); - INvDrvServices.NvMapsById.DeleteProcess(this); - INvDrvServices.NvMapsFb .DeleteProcess(this); + INvDrvServices.UnloadProcess(this); AppletState.Dispose(); diff --git a/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs index 2652724d..00209f9d 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs @@ -2,30 +2,36 @@ using ChocolArm64.Memory; using Ryujinx.Core.Logging; using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Ipc; -using Ryujinx.Core.OsHle.Utilities; -using Ryujinx.Graphics.Gpu; +using Ryujinx.Core.OsHle.Services.Nv.NvGpuAS; +using Ryujinx.Core.OsHle.Services.Nv.NvGpuGpu; +using Ryujinx.Core.OsHle.Services.Nv.NvHostChannel; +using Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl; +using Ryujinx.Core.OsHle.Services.Nv.NvMap; using System; using System.Collections.Generic; -using System.IO; namespace Ryujinx.Core.OsHle.Services.Nv { class INvDrvServices : IpcService, IDisposable { - private delegate long ServiceProcessIoctl(ServiceCtx Context); + private delegate int IoctlProcessor(ServiceCtx Context, int Cmd); private Dictionary<int, ServiceProcessRequest> m_Commands; public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; - private Dictionary<(string, int), ServiceProcessIoctl> IoctlCmds; + private static Dictionary<string, IoctlProcessor> IoctlProcessors = + new Dictionary<string, IoctlProcessor>() + { + { "/dev/nvhost-as-gpu", ProcessIoctlNvGpuAS }, + { "/dev/nvhost-ctrl", ProcessIoctlNvHostCtrl }, + { "/dev/nvhost-ctrl-gpu", ProcessIoctlNvGpuGpu }, + { "/dev/nvhost-gpu", ProcessIoctlNvHostChannel }, + { "/dev/nvmap", ProcessIoctlNvMap } + }; public static GlobalStateTable Fds { get; private set; } - public static GlobalStateTable NvMaps { get; private set; } - public static GlobalStateTable NvMapsById { get; private set; } - public static GlobalStateTable NvMapsFb { get; private set; } - private KEvent Event; public INvDrvServices() @@ -37,42 +43,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv { 2, Close }, { 3, Initialize }, { 4, QueryEvent }, - { 8, SetClientPid }, - }; - - IoctlCmds = new Dictionary<(string, int), ServiceProcessIoctl>() - { - { ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel }, - { ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace }, - { ("/dev/nvhost-as-gpu", 0x4105), NvGpuAsIoctlUnmap }, - { ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx }, - { ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions }, - { ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx }, - { ("/dev/nvhost-as-gpu", 0x4114), NvGpuAsIoctlRemap }, - { ("/dev/nvhost-ctrl", 0x001b), NvHostIoctlCtrlGetConfig }, - { ("/dev/nvhost-ctrl", 0x001d), NvHostIoctlCtrlEventWait }, - { ("/dev/nvhost-ctrl", 0x001e), NvHostIoctlCtrlEventWaitAsync }, - { ("/dev/nvhost-ctrl", 0x001f), NvHostIoctlCtrlEventRegister }, - { ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize }, - { ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo }, - { ("/dev/nvhost-ctrl-gpu", 0x4703), NvGpuIoctlZbcSetTable }, - { ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics }, - { ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks }, - { ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask }, - { ("/dev/nvhost-gpu", 0x4714), NvMapIoctlChannelSetUserData }, - { ("/dev/nvhost-gpu", 0x4801), NvMapIoctlChannelSetNvMap }, - { ("/dev/nvhost-gpu", 0x4808), NvMapIoctlChannelSubmitGpFifo }, - { ("/dev/nvhost-gpu", 0x4809), NvMapIoctlChannelAllocObjCtx }, - { ("/dev/nvhost-gpu", 0x480b), NvMapIoctlChannelZcullBind }, - { ("/dev/nvhost-gpu", 0x480c), NvMapIoctlChannelSetErrorNotifier }, - { ("/dev/nvhost-gpu", 0x480d), NvMapIoctlChannelSetPriority }, - { ("/dev/nvhost-gpu", 0x481a), NvMapIoctlChannelAllocGpFifoEx2 }, - { ("/dev/nvmap", 0x0101), NvMapIocCreate }, - { ("/dev/nvmap", 0x0103), NvMapIocFromId }, - { ("/dev/nvmap", 0x0104), NvMapIocAlloc }, - { ("/dev/nvmap", 0x0105), NvMapIocFree }, - { ("/dev/nvmap", 0x0109), NvMapIocParam }, - { ("/dev/nvmap", 0x010e), NvMapIocGetId }, + { 8, SetClientPid } }; Event = new KEvent(); @@ -81,10 +52,6 @@ namespace Ryujinx.Core.OsHle.Services.Nv static INvDrvServices() { Fds = new GlobalStateTable(); - - NvMaps = new GlobalStateTable(); - NvMapsById = new GlobalStateTable(); - NvMapsFb = new GlobalStateTable(); } public long Open(ServiceCtx Context) @@ -104,22 +71,25 @@ namespace Ryujinx.Core.OsHle.Services.Nv public long Ioctl(ServiceCtx Context) { int Fd = Context.RequestData.ReadInt32(); - int Cmd = Context.RequestData.ReadInt32() & 0xffff; + int Cmd = Context.RequestData.ReadInt32(); NvFd FdData = Fds.GetData<NvFd>(Context.Process, Fd); - long Position = Context.Request.GetSendBuffPtr(); - - Context.ResponseData.Write(0); + int Result; - if (IoctlCmds.TryGetValue((FdData.Name, Cmd), out ServiceProcessIoctl ProcReq)) + if (IoctlProcessors.TryGetValue(FdData.Name, out IoctlProcessor Process)) { - return ProcReq(Context); + Result = Process(Context, Cmd); } else { throw new NotImplementedException($"{FdData.Name} {Cmd:x4}"); } + + //TODO: Verify if the error codes needs to be translated. + Context.ResponseData.Write(Result); + + return 0; } public long Close(ServiceCtx Context) @@ -138,9 +108,9 @@ namespace Ryujinx.Core.OsHle.Services.Nv long TransferMemSize = Context.RequestData.ReadInt64(); int TransferMemHandle = Context.Request.HandleDesc.ToCopy[0]; - Context.ResponseData.Write(0); + NvMapIoctl.InitializeNvMap(Context); - NvMapsFb.Add(Context.Process, 0, new NvMapFb()); + Context.ResponseData.Write(0); return 0; } @@ -169,659 +139,69 @@ namespace Ryujinx.Core.OsHle.Services.Nv return 0; } - private long NvGpuAsIoctlBindChannel(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - int Fd = Context.Memory.ReadInt32(Position); - - return 0; - } - - private long NvGpuAsIoctlAllocSpace(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int Pages = Reader.ReadInt32(); - int PageSize = Reader.ReadInt32(); - int Flags = Reader.ReadInt32(); - int Padding = Reader.ReadInt32(); - long Align = Reader.ReadInt64(); - - if ((Flags & 1) != 0) - { - Align = Context.Ns.Gpu.ReserveMemory(Align, (long)Pages * PageSize, 1); - } - else - { - Align = Context.Ns.Gpu.ReserveMemory((long)Pages * PageSize, Align); - } - - Context.Memory.WriteInt64(Position + 0x10, Align); - - return 0; - } - - private long NvGpuAsIoctlUnmap(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - long Offset = Reader.ReadInt64(); - - Context.Ns.Gpu.MemoryMgr.Unmap(Offset, 0x10000); - - return 0; - } - - private long NvGpuAsIoctlMapBufferEx(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int Flags = Reader.ReadInt32(); - int Kind = Reader.ReadInt32(); - int Handle = Reader.ReadInt32(); - int PageSize = Reader.ReadInt32(); - long BuffAddr = Reader.ReadInt64(); - long MapSize = Reader.ReadInt64(); - long Offset = Reader.ReadInt64(); - - if (Handle == 0) - { - //This is used to store offsets for the Framebuffer(s); - NvMapFb MapFb = (NvMapFb)NvMapsFb.GetData(Context.Process, 0); - - MapFb.AddBufferOffset(BuffAddr); - - return 0; - } - - NvMap Map = NvMaps.GetData<NvMap>(Context.Process, Handle); - - if (Map == null) - { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"invalid NvMap Handle {Handle}!"); - - return -1; //TODO: Corrent error code. - } - - if ((Flags & 1) != 0) - { - Offset = Context.Ns.Gpu.MapMemory(Map.CpuAddress, Offset, Map.Size); - } - else - { - Offset = Context.Ns.Gpu.MapMemory(Map.CpuAddress, Map.Size); - } - - Context.Memory.WriteInt64(Position + 0x20, Offset); - - Map.GpuAddress = Offset; - - return 0; - } - - private long NvGpuAsIoctlGetVaRegions(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - MemWriter Writer = new MemWriter(Context.Memory, Position); - - long Unused = Reader.ReadInt64(); - int BuffSize = Reader.ReadInt32(); - int Padding = Reader.ReadInt32(); - - BuffSize = 0x30; - - Writer.WriteInt64(Unused); - Writer.WriteInt32(BuffSize); - Writer.WriteInt32(Padding); - - Writer.WriteInt64(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt64(0); - - Writer.WriteInt64(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt64(0); - - return 0; - } - - private long NvGpuAsIoctlInitializeEx(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int BigPageSize = Reader.ReadInt32(); - int AsFd = Reader.ReadInt32(); - int Flags = Reader.ReadInt32(); - int Reserved = Reader.ReadInt32(); - long Unknown10 = Reader.ReadInt64(); - long Unknown18 = Reader.ReadInt64(); - long Unknown20 = Reader.ReadInt64(); - - return 0; - } - - private long NvGpuAsIoctlRemap(ServiceCtx Context) - { - Context.RequestData.BaseStream.Seek(-4, SeekOrigin.Current); - - int Cmd = Context.RequestData.ReadInt32(); - - int Size = (Cmd >> 16) & 0xff; - - int Count = Size / 0x18; - - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - for (int Index = 0; Index < Count; Index++) - { - int Flags = Reader.ReadInt32(); - int Kind = Reader.ReadInt32(); - int Handle = Reader.ReadInt32(); - int Padding = Reader.ReadInt32(); - int Offset = Reader.ReadInt32(); - int Pages = Reader.ReadInt32(); - - NvMap Map = NvMaps.GetData<NvMap>(Context.Process, Handle); - - if (Map == null) - { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"invalid NvMap Handle {Handle}!"); - - return -1; //TODO: Corrent error code. - } - - Context.Ns.Gpu.MapMemory(Map.CpuAddress, - (long)(uint)Offset << 16, - (long)(uint)Pages << 16); - } - - //TODO - - return 0; - } - - private long NvHostIoctlCtrlGetConfig(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - MemWriter Writer = new MemWriter(Context.Memory, Position + 0x82); - - for (int Index = 0; Index < 0x101; Index++) - { - Writer.WriteByte(0); - } - - return 0; - } - - private long NvHostIoctlCtrlEventWait(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int SyncPtId = Reader.ReadInt32(); - int Threshold = Reader.ReadInt32(); - int Timeout = Reader.ReadInt32(); - int Value = Reader.ReadInt32(); - - Context.Memory.WriteInt32(Position + 0xc, 0xcafe); - - return 0; - } - - private long NvHostIoctlCtrlEventWaitAsync(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int SyncPtId = Reader.ReadInt32(); - int Threshold = Reader.ReadInt32(); - int Timeout = Reader.ReadInt32(); - int Value = Reader.ReadInt32(); - - Context.Memory.WriteInt32(Position + 0xc, 0xcafe); - - return 0; - } - - private long NvHostIoctlCtrlEventRegister(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int UserEventId = Reader.ReadInt32(); - - return 0; - } - - private long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - Context.Memory.WriteInt32(Position, 1); - - return 0; - } - - private long NvGpuIoctlZcullGetInfo(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemWriter Writer = new MemWriter(Context.Memory, Position); - - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - Writer.WriteInt32(0); - - return 0; - } - - private long NvGpuIoctlZbcSetTable(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int[] ColorDs = new int[4]; - int[] ColorL2 = new int[4]; - - ColorDs[0] = Reader.ReadInt32(); - ColorDs[1] = Reader.ReadInt32(); - ColorDs[2] = Reader.ReadInt32(); - ColorDs[3] = Reader.ReadInt32(); - - ColorL2[0] = Reader.ReadInt32(); - ColorL2[1] = Reader.ReadInt32(); - ColorL2[2] = Reader.ReadInt32(); - ColorL2[3] = Reader.ReadInt32(); - - int Depth = Reader.ReadInt32(); - int Format = Reader.ReadInt32(); - int Type = Reader.ReadInt32(); - - return 0; - } - - private long NvGpuIoctlGetCharacteristics(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - MemWriter Writer = new MemWriter(Context.Memory, Position); - - //Note: We should just ignore the BuffAddr, because official code - //does __memcpy_device from Position + 0x10 to BuffAddr. - long BuffSize = Reader.ReadInt64(); - long BuffAddr = Reader.ReadInt64(); - - BuffSize = 0xa0; - - Writer.WriteInt64(BuffSize); - Writer.WriteInt64(BuffAddr); - Writer.WriteInt32(0x120); //NVGPU_GPU_ARCH_GM200 - Writer.WriteInt32(0xb); //NVGPU_GPU_IMPL_GM20B - Writer.WriteInt32(0xa1); - Writer.WriteInt32(1); - Writer.WriteInt64(0x40000); - Writer.WriteInt64(0); - Writer.WriteInt32(2); - Writer.WriteInt32(0x20); //NVGPU_GPU_BUS_TYPE_AXI - Writer.WriteInt32(0x20000); - Writer.WriteInt32(0x20000); - Writer.WriteInt32(0x1b); - Writer.WriteInt32(0x30000); - Writer.WriteInt32(1); - Writer.WriteInt32(0x503); - Writer.WriteInt32(0x503); - Writer.WriteInt32(0x80); - Writer.WriteInt32(0x28); - Writer.WriteInt32(0); - Writer.WriteInt64(0x55); - Writer.WriteInt32(0x902d); //FERMI_TWOD_A - Writer.WriteInt32(0xb197); //MAXWELL_B - Writer.WriteInt32(0xb1c0); //MAXWELL_COMPUTE_B - Writer.WriteInt32(0xb06f); //MAXWELL_CHANNEL_GPFIFO_A - Writer.WriteInt32(0xa140); //KEPLER_INLINE_TO_MEMORY_B - Writer.WriteInt32(0xb0b5); //MAXWELL_DMA_COPY_A - Writer.WriteInt32(1); - Writer.WriteInt32(0); - Writer.WriteInt32(2); - Writer.WriteInt32(1); - Writer.WriteInt32(0); - Writer.WriteInt32(1); - Writer.WriteInt32(0x21d70); - Writer.WriteInt32(0); - Writer.WriteByte((byte)'g'); - Writer.WriteByte((byte)'m'); - Writer.WriteByte((byte)'2'); - Writer.WriteByte((byte)'0'); - Writer.WriteByte((byte)'b'); - Writer.WriteByte((byte)'\0'); - Writer.WriteByte((byte)'\0'); - Writer.WriteByte((byte)'\0'); - Writer.WriteInt64(0); - - return 0; - } - - private long NvGpuIoctlGetTpcMasks(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int MaskBuffSize = Reader.ReadInt32(); - int Reserved = Reader.ReadInt32(); - long MaskBuffAddr = Reader.ReadInt64(); - long Unknown = Reader.ReadInt64(); - - return 0; - } - - private long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - Context.Memory.WriteInt32(Position + 0, 7); - Context.Memory.WriteInt32(Position + 4, 1); - - return 0; - } - - private long NvMapIoctlChannelSetUserData(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - return 0; - } - - private long NvMapIoctlChannelSetNvMap(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - int Fd = Context.Memory.ReadInt32(Position); - - return 0; - } - - private long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - MemWriter Writer = new MemWriter(Context.Memory, Position + 0x10); - - long GpFifo = Reader.ReadInt64(); - int Count = Reader.ReadInt32(); - int Flags = Reader.ReadInt32(); - int FenceId = Reader.ReadInt32(); - int FenceVal = Reader.ReadInt32(); - - for (int Index = 0; Index < Count; Index++) - { - long GpFifoHdr = Reader.ReadInt64(); - - long GpuAddr = GpFifoHdr & 0xffffffffff; - - int Size = (int)(GpFifoHdr >> 40) & 0x7ffffc; - - long CpuAddr = Context.Ns.Gpu.GetCpuAddr(GpuAddr); - - if (CpuAddr != -1) - { - byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, CpuAddr, Size); - - NsGpuPBEntry[] PushBuffer = NvGpuPushBuffer.Decode(Data); - - Context.Ns.Gpu.Fifo.PushBuffer(Context.Memory, PushBuffer); - } - } - - Writer.WriteInt32(0); - Writer.WriteInt32(0); - - return 0; - } - - private long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - int ClassNum = Context.Memory.ReadInt32(Position + 0); - int Flags = Context.Memory.ReadInt32(Position + 4); - - Context.Memory.WriteInt32(Position + 8, 0); - - return 0; - } - - private long NvMapIoctlChannelZcullBind(ServiceCtx Context) + private static int ProcessIoctlNvGpuAS(ServiceCtx Context, int Cmd) { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - long GpuVa = Reader.ReadInt64(); - int Mode = Reader.ReadInt32(); - int Padding = Reader.ReadInt32(); - - return 0; + return ProcessIoctl(Context, Cmd, NvGpuASIoctl.ProcessIoctl); } - private long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context) + private static int ProcessIoctlNvHostCtrl(ServiceCtx Context, int Cmd) { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - long Offset = Reader.ReadInt64(); - long Size = Reader.ReadInt64(); - int Mem = Reader.ReadInt32(); - int Padding = Reader.ReadInt32(); - - return 0; + return ProcessIoctl(Context, Cmd, NvHostCtrlIoctl.ProcessIoctl); } - private long NvMapIoctlChannelSetPriority(ServiceCtx Context) + private static int ProcessIoctlNvGpuGpu(ServiceCtx Context, int Cmd) { - long Position = Context.Request.GetSendBuffPtr(); - - int Priority = Context.Memory.ReadInt32(Position); - - return 0; + return ProcessIoctl(Context, Cmd, NvGpuGpuIoctl.ProcessIoctl); } - private long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context) + private static int ProcessIoctlNvHostChannel(ServiceCtx Context, int Cmd) { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - MemWriter Writer = new MemWriter(Context.Memory, Position + 0xc); - - int Count = Reader.ReadInt32(); - int Flags = Reader.ReadInt32(); - int Unknown8 = Reader.ReadInt32(); - long Fence = Reader.ReadInt64(); - int Unknown14 = Reader.ReadInt32(); - int Unknown18 = Reader.ReadInt32(); - - Writer.WriteInt32(0); - Writer.WriteInt32(0); - - return 0; + return ProcessIoctl(Context, Cmd, NvHostChannelIoctl.ProcessIoctl); } - private long NvMapIocCreate(ServiceCtx Context) + private static int ProcessIoctlNvMap(ServiceCtx Context, int Cmd) { - long Position = Context.Request.GetSendBuffPtr(); - - int Size = Context.Memory.ReadInt32(Position); - - NvMap Map = new NvMap() { Size = Size }; - - Map.Handle = NvMaps.Add(Context.Process, Map); - - Map.Id = NvMapsById.Add(Context.Process, Map); - - Context.Memory.WriteInt32(Position + 4, Map.Handle); - - Context.Ns.Log.PrintInfo(LogClass.ServiceNv, $"NvMap {Map.Id} created with size {Size:x8}!"); - - return 0; + return ProcessIoctl(Context, Cmd, NvMapIoctl.ProcessIoctl); } - private long NvMapIocFromId(ServiceCtx Context) + private static int ProcessIoctl(ServiceCtx Context, int Cmd, IoctlProcessor Processor) { - long Position = Context.Request.GetSendBuffPtr(); - - int Id = Context.Memory.ReadInt32(Position); - - NvMap Map = NvMapsById.GetData<NvMap>(Context.Process, Id); - - if (Map == null) + if (CmdIn(Cmd) && Context.Request.GetBufferType0x21Position() == 0) { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap Id {Id}!"); + Context.Ns.Log.PrintError(LogClass.ServiceNv, "Input buffer is null!"); - return -1; //TODO: Corrent error code. + return NvResult.InvalidInput; } - Context.Memory.WriteInt32(Position + 4, Map.Handle); - - return 0; - } - - private long NvMapIocAlloc(ServiceCtx Context) - { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int Handle = Reader.ReadInt32(); - int HeapMask = Reader.ReadInt32(); - int Flags = Reader.ReadInt32(); - int Align = Reader.ReadInt32(); - byte Kind = (byte)Reader.ReadInt64(); - long Addr = Reader.ReadInt64(); - - NvMap Map = NvMaps.GetData<NvMap>(Context.Process, Handle); - - if (Map == null) + if (CmdOut(Cmd) && Context.Request.GetBufferType0x22Position() == 0) { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap Handle {Handle}!"); + Context.Ns.Log.PrintError(LogClass.ServiceNv, "Output buffer is null!"); - return -1; //TODO: Corrent error code. + return NvResult.InvalidInput; } - Map.CpuAddress = Addr; - Map.Align = Align; - Map.Kind = Kind; - - return 0; + return Processor(Context, Cmd); } - private long NvMapIocFree(ServiceCtx Context) + private static bool CmdIn(int Cmd) { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - MemWriter Writer = new MemWriter(Context.Memory, Position + 8); - - int Handle = Reader.ReadInt32(); - int Padding = Reader.ReadInt32(); - - NvMap Map = NvMaps.GetData<NvMap>(Context.Process, Handle); - - if (Map == null) - { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap Handle {Handle}!"); - - return -1; //TODO: Corrent error code. - } - - Writer.WriteInt64(0); - Writer.WriteInt32(Map.Size); - Writer.WriteInt32(0); - - return 0; + return ((Cmd >> 30) & 1) != 0; } - private long NvMapIocParam(ServiceCtx Context) + private static bool CmdOut(int Cmd) { - long Position = Context.Request.GetSendBuffPtr(); - - MemReader Reader = new MemReader(Context.Memory, Position); - - int Handle = Reader.ReadInt32(); - int Param = Reader.ReadInt32(); - - NvMap Map = NvMaps.GetData<NvMap>(Context.Process, Handle); - - if (Map == null) - { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap Handle {Handle}!"); - - return -1; //TODO: Corrent error code. - } - - int Response = 0; - - switch (Param) - { - case 1: Response = Map.Size; break; - case 2: Response = Map.Align; break; - case 4: Response = 0x40000000; break; - case 5: Response = Map.Kind; break; - } - - Context.Memory.WriteInt32(Position + 8, Response); - - return 0; + return ((Cmd >> 31) & 1) != 0; } - private long NvMapIocGetId(ServiceCtx Context) + public static void UnloadProcess(Process Process) { - long Position = Context.Request.GetSendBuffPtr(); - - int Handle = Context.Memory.ReadInt32(Position + 4); - - NvMap Map = NvMaps.GetData<NvMap>(Context.Process, Handle); + Fds.DeleteProcess(Process); - if (Map == null) - { - Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap Handle {Handle}!"); - - return -1; //TODO: Corrent error code. - } + NvGpuASIoctl.UnloadProcess(Process); - Context.Memory.WriteInt32(Position, Map.Id); + NvHostCtrlIoctl.UnloadProcess(Process); - return 0; + NvMapIoctl.UnloadProcess(Process); } public void Dispose() diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvChNvMap.cs b/Ryujinx.Core/OsHle/Services/Nv/NvChNvMap.cs deleted file mode 100644 index 9ea3ae6e..00000000 --- a/Ryujinx.Core/OsHle/Services/Nv/NvChNvMap.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Concurrent; - -namespace Ryujinx.Core.OsHle.Services.Nv -{ - class NvChNvMap - { - private static ConcurrentDictionary<Process, IdDictionary> NvMaps; - - public void Create(ServiceCtx Context) - { - long InputPosition = Context.Request.GetBufferType0x21Position(); - long OutputPosition = Context.Request.GetBufferType0x22Position(); - - int Size = Context.Memory.ReadInt32(InputPosition); - - int Handle = AddNvMap(Context, new NvMap(Size)); - - Context.Memory.WriteInt32(OutputPosition, Handle); - } - - private int AddNvMap(ServiceCtx Context, NvMap Map) - { - return NvMaps[Context.Process].Add(Map); - } - - public NvMap GetNvMap(ServiceCtx Context, int Handle) - { - return NvMaps[Context.Process].GetData<NvMap>(Handle); - } - } -}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASAllocSpace.cs b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASAllocSpace.cs new file mode 100644 index 00000000..7e652d1f --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASAllocSpace.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvGpuAS +{ + struct NvGpuASAllocSpace + { + public int Pages; + public int PageSize; + public int Flags; + public int Padding; + public long Offset; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs new file mode 100644 index 00000000..6be45d5c --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs @@ -0,0 +1,245 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.Gpu; +using Ryujinx.Core.Logging; +using Ryujinx.Core.OsHle.Services.Nv.NvMap; +using System; +using System.Collections.Concurrent; + +namespace Ryujinx.Core.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.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int AllocSpace(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + 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.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + 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.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + 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.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + 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 = Map.Size; + } + + Size = 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.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int InitializeEx(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int Remap(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + + 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 diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASMapBufferEx.cs b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASMapBufferEx.cs new file mode 100644 index 00000000..b8bdd706 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASMapBufferEx.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvGpuAS +{ + struct NvGpuASMapBufferEx + { + public int Flags; + public int Kind; + public int NvMapHandle; + public int PageSize; + public long BufferOffset; + public long MappingSize; + public long Offset; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASRemap.cs b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASRemap.cs new file mode 100644 index 00000000..363ae687 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASRemap.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvGpuAS +{ + struct NvGpuASRemap + { + public short Flags; + public short Kind; + public int NvMapHandle; + public int Padding; + public int Offset; + public int Pages; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASUnmapBuffer.cs b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASUnmapBuffer.cs new file mode 100644 index 00000000..8b627511 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvGpuAS/NvGpuASUnmapBuffer.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvGpuAS +{ + struct NvGpuASUnmapBuffer + { + public long Offset; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuGetCharacteristics.cs b/Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuGetCharacteristics.cs new file mode 100644 index 00000000..5d92b508 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuGetCharacteristics.cs @@ -0,0 +1,43 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvGpuGpu +{ + struct NvGpuGpuGetCharacteristics + { + public long BufferSize; + public long BufferAddress; + public int Arch; + public int Impl; + public int Rev; + public int NumGpc; + public long L2CacheSize; + public long OnBoardVideoMemorySize; + public int NumTpcPerGpc; + public int BusType; + public int BigPageSize; + public int CompressionPageSize; + public int PdeCoverageBitCount; + public int AvailableBigPageSizes; + public int GpcMask; + public int SmArchSmVersion; + public int SmArchSpaVersion; + public int SmArchWarpCount; + public int GpuVaBitCount; + public int Reserved; + public long Flags; + public int TwodClass; + public int ThreedClass; + public int ComputeClass; + public int GpfifoClass; + public int InlineToMemoryClass; + public int DmaCopyClass; + public int MaxFbpsCount; + public int FbpEnMask; + public int MaxLtcPerFbp; + public int MaxLtsPerLtc; + public int MaxTexPerTpc; + public int MaxGpcCount; + public int RopL2EnMask0; + public int RopL2EnMask1; + public long ChipName; + public long GrCompbitStoreBaseHw; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs b/Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs new file mode 100644 index 00000000..772b6786 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs @@ -0,0 +1,155 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.Logging; +using System; +using System.Diagnostics; + +namespace Ryujinx.Core.OsHle.Services.Nv.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 InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int ZcullGetInfo(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int ZbcSetTable(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int GetCharacteristics(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvGpuGpuGetCharacteristics Args = AMemoryHelper.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; + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static int GetTpcMasks(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int GetActiveSlotMask(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int GetGpuTime(ServiceCtx Context) + { + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Memory.WriteInt64(OutputPosition, GetPTimerNanoSeconds()); + + return NvResult.Success; + } + + private static long GetPTimerNanoSeconds() + { + double Ticks = PTimer.ElapsedTicks; + + return (long)(Ticks * TicksToNs) & 0xff_ffff_ffff_ffff; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHelper.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHelper.cs new file mode 100644 index 00000000..c5cee361 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHelper.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Core.OsHle.Services.Nv +{ + static class NvHelper + { + public static void Crash() + { + + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs new file mode 100644 index 00000000..85b47533 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs @@ -0,0 +1,130 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.Logging; +using Ryujinx.Core.OsHle.Services.Nv.NvGpuAS; +using Ryujinx.Core.Gpu; +using System; + +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostChannel +{ + class NvHostChannelIoctl + { + public static int ProcessIoctl(ServiceCtx Context, int Cmd) + { + switch (Cmd & 0xffff) + { + case 0x4714: return SetUserData (Context); + case 0x4801: return SetNvMap (Context); + case 0x4808: return SubmitGpfifo (Context); + case 0x4809: return AllocObjCtx (Context); + case 0x480b: return ZcullBind (Context); + case 0x480c: return SetErrorNotifier(Context); + case 0x480d: return SetPriority (Context); + case 0x481a: return AllocGpfifoEx2 (Context); + } + + throw new NotImplementedException(Cmd.ToString("x8")); + } + + private static int SetUserData(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int SetNvMap(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int SubmitGpfifo(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvHostChannelSubmitGpfifo Args = AMemoryHelper.Read<NvHostChannelSubmitGpfifo>(Context.Memory, InputPosition); + + NvGpuVmm Vmm = NvGpuASIoctl.GetVmm(Context); + + for (int Index = 0; Index < Args.NumEntries; Index++) + { + long Gpfifo = Context.Memory.ReadInt64(InputPosition + 0x18 + Index * 8); + + long VA = Gpfifo & 0xff_ffff_ffff; + + int Size = (int)(Gpfifo >> 40) & 0x7ffffc; + + byte[] Data = Vmm.ReadBytes(VA, Size); + + NvGpuPBEntry[] PushBuffer = NvGpuPushBuffer.Decode(Data); + + Context.Ns.Gpu.Fifo.PushBuffer(Vmm, PushBuffer); + } + + Args.SyncptId = 0; + Args.SyncptValue = 0; + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static int AllocObjCtx(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int ZcullBind(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int SetErrorNotifier(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int SetPriority(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int AllocGpfifoEx2(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelSubmitGpfifo.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelSubmitGpfifo.cs new file mode 100644 index 00000000..4698a3da --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostChannel/NvHostChannelSubmitGpfifo.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostChannel +{ + struct NvHostChannelSubmitGpfifo + { + public long Gpfifo; + public int NumEntries; + public int Flags; + public int SyncptId; + public int SyncptValue; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs new file mode 100644 index 00000000..8c705d7f --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs @@ -0,0 +1,355 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.Logging; +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + class NvHostCtrlIoctl + { + private static ConcurrentDictionary<Process, NvHostCtrlUserCtx> UserCtxs; + + static NvHostCtrlIoctl() + { + UserCtxs = new ConcurrentDictionary<Process, NvHostCtrlUserCtx>(); + } + + public static int ProcessIoctl(ServiceCtx Context, int Cmd) + { + switch (Cmd & 0xffff) + { + case 0x0014: return SyncptRead (Context); + case 0x0015: return SyncptIncr (Context); + case 0x0016: return SyncptWait (Context); + case 0x0019: return SyncptWaitEx (Context); + case 0x001a: return SyncptReadMax (Context); + case 0x001b: return GetConfig (Context); + case 0x001d: return EventWait (Context); + case 0x001e: return EventWaitAsync(Context); + case 0x001f: return EventRegister (Context); + } + + throw new NotImplementedException(Cmd.ToString("x8")); + } + + private static int SyncptRead(ServiceCtx Context) + { + return SyncptReadMinOrMax(Context, Max: false); + } + + private static int SyncptIncr(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + + int Id = Context.Memory.ReadInt32(InputPosition); + + if ((uint)Id >= NvHostSyncpt.SyncptsCount) + { + return NvResult.InvalidInput; + } + + GetUserCtx(Context).Syncpt.Increment(Id); + + return NvResult.Success; + } + + private static int SyncptWait(ServiceCtx Context) + { + return SyncptWait(Context, Extended: false); + } + + private static int SyncptWaitEx(ServiceCtx Context) + { + return SyncptWait(Context, Extended: true); + } + + private static int SyncptReadMax(ServiceCtx Context) + { + return SyncptReadMinOrMax(Context, Max: true); + } + + private static int GetConfig(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + string Nv = AMemoryHelper.ReadAsciiString(Context.Memory, InputPosition + 0, 0x41); + string Name = AMemoryHelper.ReadAsciiString(Context.Memory, InputPosition + 0x41, 0x41); + + Context.Memory.WriteByte(OutputPosition + 0x82, 0); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int EventWait(ServiceCtx Context) + { + return EventWait(Context, Async: false); + } + + private static int EventWaitAsync(ServiceCtx Context) + { + return EventWait(Context, Async: true); + } + + private static int EventRegister(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + int EventId = Context.Memory.ReadInt32(InputPosition); + + Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + + return NvResult.Success; + } + + private static int SyncptReadMinOrMax(ServiceCtx Context, bool Max) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvHostCtrlSyncptRead Args = AMemoryHelper.Read<NvHostCtrlSyncptRead>(Context.Memory, InputPosition); + + if ((uint)Args.Id >= NvHostSyncpt.SyncptsCount) + { + return NvResult.InvalidInput; + } + + if (Max) + { + Args.Value = GetUserCtx(Context).Syncpt.GetMax(Args.Id); + } + else + { + Args.Value = GetUserCtx(Context).Syncpt.GetMin(Args.Id); + } + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static int SyncptWait(ServiceCtx Context, bool Extended) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvHostCtrlSyncptWait Args = AMemoryHelper.Read<NvHostCtrlSyncptWait>(Context.Memory, InputPosition); + + NvHostSyncpt Syncpt = GetUserCtx(Context).Syncpt; + + if ((uint)Args.Id >= NvHostSyncpt.SyncptsCount) + { + return NvResult.InvalidInput; + } + + int Result; + + if (Syncpt.MinCompare(Args.Id, Args.Thresh)) + { + Result = NvResult.Success; + } + else if (Args.Timeout == 0) + { + Result = NvResult.TryAgain; + } + else + { + Context.Ns.Log.PrintDebug(LogClass.ServiceNv, "Waiting syncpt with timeout of " + Args.Timeout + "ms..."); + + using (ManualResetEvent WaitEvent = new ManualResetEvent(false)) + { + Syncpt.AddWaiter(Args.Thresh, WaitEvent); + + //Note: Negative (> INT_MAX) timeouts aren't valid on .NET, + //in this case we just use the maximum timeout possible. + int Timeout = Args.Timeout; + + if (Timeout < -1) + { + Timeout = int.MaxValue; + } + + if (Timeout == -1) + { + WaitEvent.WaitOne(); + + Result = NvResult.Success; + } + else if (WaitEvent.WaitOne(Timeout)) + { + Result = NvResult.Success; + } + else + { + Result = NvResult.TimedOut; + } + } + + Context.Ns.Log.PrintDebug(LogClass.ServiceNv, "Resuming..."); + } + + if (Extended) + { + Context.Memory.WriteInt32(OutputPosition + 0xc, Syncpt.GetMin(Args.Id)); + } + + return Result; + } + + private static int EventWait(ServiceCtx Context, bool Async) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvHostCtrlSyncptWaitEx Args = AMemoryHelper.Read<NvHostCtrlSyncptWaitEx>(Context.Memory, InputPosition); + + if ((uint)Args.Id >= NvHostSyncpt.SyncptsCount) + { + return NvResult.InvalidInput; + } + + void WriteArgs() + { + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + } + + NvHostSyncpt Syncpt = GetUserCtx(Context).Syncpt; + + if (Syncpt.MinCompare(Args.Id, Args.Thresh)) + { + Args.Value = Syncpt.GetMin(Args.Id); + + WriteArgs(); + + return NvResult.Success; + } + + if (!Async) + { + Args.Value = 0; + } + + if (Args.Timeout == 0) + { + WriteArgs(); + + return NvResult.TryAgain; + } + + NvHostEvent Event; + + int Result, EventIndex; + + if (Async) + { + EventIndex = Args.Value; + + if ((uint)EventIndex >= NvHostCtrlUserCtx.EventsCount) + { + return NvResult.InvalidInput; + } + + Event = GetUserCtx(Context).Events[EventIndex]; + } + else + { + Event = GetFreeEvent(Context, Syncpt, Args.Id, out EventIndex); + } + + if (Event != null && + (Event.State == NvHostEventState.Registered || + Event.State == NvHostEventState.Free)) + { + Event.Id = Args.Id; + Event.Thresh = Args.Thresh; + + Event.State = NvHostEventState.Waiting; + + if (!Async) + { + Args.Value = ((Args.Id & 0xfff) << 16) | 0x10000000; + } + else + { + Args.Value = Args.Id << 4; + } + + Args.Value |= EventIndex; + + Result = NvResult.TryAgain; + } + else + { + Result = NvResult.InvalidInput; + } + + WriteArgs(); + + return Result; + } + + private static NvHostEvent GetFreeEvent( + ServiceCtx Context, + NvHostSyncpt Syncpt, + int Id, + out int EventIndex) + { + NvHostEvent[] Events = GetUserCtx(Context).Events; + + EventIndex = NvHostCtrlUserCtx.EventsCount; + + int NullIndex = NvHostCtrlUserCtx.EventsCount; + + for (int Index = 0; Index < NvHostCtrlUserCtx.EventsCount; Index++) + { + NvHostEvent Event = Events[Index]; + + if (Event != null) + { + if (Event.State == NvHostEventState.Registered || + Event.State == NvHostEventState.Free) + { + EventIndex = Index; + + if (Event.Id == Id) + { + return Event; + } + } + } + else if (NullIndex == NvHostCtrlUserCtx.EventsCount) + { + NullIndex = Index; + } + } + + if (NullIndex < NvHostCtrlUserCtx.EventsCount) + { + EventIndex = NullIndex; + + return Events[NullIndex] = new NvHostEvent(); + } + + if (EventIndex < NvHostCtrlUserCtx.EventsCount) + { + return Events[EventIndex]; + } + + return null; + } + + public static NvHostCtrlUserCtx GetUserCtx(ServiceCtx Context) + { + return UserCtxs.GetOrAdd(Context.Process, (Key) => new NvHostCtrlUserCtx()); + } + + public static void UnloadProcess(Process Process) + { + UserCtxs.TryRemove(Process, out _); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtRead.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtRead.cs new file mode 100644 index 00000000..0b05e63a --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtRead.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + struct NvHostCtrlSyncptRead + { + public int Id; + public int Value; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWait.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWait.cs new file mode 100644 index 00000000..6746090a --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWait.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + struct NvHostCtrlSyncptWait + { + public int Id; + public int Thresh; + public int Timeout; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWaitEx.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWaitEx.cs new file mode 100644 index 00000000..21ba3783 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlSyncPtWaitEx.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + struct NvHostCtrlSyncptWaitEx + { + public int Id; + public int Thresh; + public int Timeout; + public int Value; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlUserCtx.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlUserCtx.cs new file mode 100644 index 00000000..be71b225 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlUserCtx.cs @@ -0,0 +1,19 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + class NvHostCtrlUserCtx + { + public const int LocksCount = 16; + public const int EventsCount = 64; + + public NvHostSyncpt Syncpt { get; private set; } + + public NvHostEvent[] Events { get; private set; } + + public NvHostCtrlUserCtx() + { + Syncpt = new NvHostSyncpt(); + + Events = new NvHostEvent[EventsCount]; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEvent.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEvent.cs new file mode 100644 index 00000000..027e25b0 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEvent.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + class NvHostEvent + { + public int Id; + public int Thresh; + + public NvHostEventState State; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEventState.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEventState.cs new file mode 100644 index 00000000..a4bd2cb6 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostEventState.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + enum NvHostEventState + { + Registered = 0, + Waiting = 1, + Busy = 2, + Free = 5 + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostSyncPt.cs b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostSyncPt.cs new file mode 100644 index 00000000..96ae16a3 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvHostCtrl/NvHostSyncPt.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.Core.OsHle.Services.Nv.NvHostCtrl +{ + class NvHostSyncpt + { + public const int SyncptsCount = 192; + + private int[] CounterMin; + private int[] CounterMax; + + private long EventMask; + + private ConcurrentDictionary<EventWaitHandle, int> Waiters; + + public NvHostSyncpt() + { + CounterMin = new int[SyncptsCount]; + CounterMax = new int[SyncptsCount]; + + Waiters = new ConcurrentDictionary<EventWaitHandle, int>(); + } + + public int GetMin(int Id) + { + return CounterMin[Id]; + } + + public int GetMax(int Id) + { + return CounterMax[Id]; + } + + public int Increment(int Id) + { + if (((EventMask >> Id) & 1) != 0) + { + Interlocked.Increment(ref CounterMax[Id]); + } + + return IncrementMin(Id); + } + + public int IncrementMin(int Id) + { + int Value = Interlocked.Increment(ref CounterMin[Id]); + + WakeUpWaiters(Id, Value); + + return Value; + } + + public int IncrementMax(int Id) + { + return Interlocked.Increment(ref CounterMax[Id]); + } + + public void AddWaiter(int Threshold, EventWaitHandle WaitEvent) + { + if (!Waiters.TryAdd(WaitEvent, Threshold)) + { + throw new InvalidOperationException(); + } + } + + public bool RemoveWaiter(EventWaitHandle WaitEvent) + { + return Waiters.TryRemove(WaitEvent, out _); + } + + private void WakeUpWaiters(int Id, int NewValue) + { + foreach (KeyValuePair<EventWaitHandle, int> KV in Waiters) + { + if (MinCompare(Id, NewValue, CounterMax[Id], KV.Value)) + { + KV.Key.Set(); + + Waiters.TryRemove(KV.Key, out _); + } + } + } + + public bool MinCompare(int Id, int Threshold) + { + return MinCompare(Id, CounterMin[Id], CounterMax[Id], Threshold); + } + + private bool MinCompare(int Id, int Min, int Max, int Threshold) + { + int MinDiff = Min - Threshold; + int MaxDiff = Max - Threshold; + + if (((EventMask >> Id) & 1) != 0) + { + return MinDiff >= 0; + } + else + { + return (uint)MaxDiff >= (uint)MinDiff; + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs deleted file mode 100644 index 570cef68..00000000 --- a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Ryujinx.Core.OsHle.Services.Nv -{ - class NvMap - { - public int Handle; - public int Id; - public int Size; - public int Align; - public int Kind; - public long CpuAddress; - public long GpuAddress; - - public NvMap() { } - - public NvMap(int Size) - { - this.Size = Size; - } - } -}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapAlloc.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapAlloc.cs new file mode 100644 index 00000000..86e4c238 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapAlloc.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + struct NvMapAlloc + { + public int Handle; + public int HeapMask; + public int Flags; + public int Align; + public long Kind; + public long Address; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapCreate.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapCreate.cs new file mode 100644 index 00000000..7d35731c --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapCreate.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + struct NvMapCreate + { + public int Size; + public int Handle; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFree.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFree.cs new file mode 100644 index 00000000..ee8bc618 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFree.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + struct NvMapFree + { + public int Handle; + public int Padding; + public long RefCount; + public int Size; + public int Flags; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFromId.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFromId.cs new file mode 100644 index 00000000..377eaa7f --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapFromId.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + struct NvMapFromId + { + public int Id; + public int Handle; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapGetId.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapGetId.cs new file mode 100644 index 00000000..639a5fb4 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapGetId.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + struct NvMapGetId + { + public int Id; + public int Handle; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandle.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandle.cs new file mode 100644 index 00000000..4a021f6f --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandle.cs @@ -0,0 +1,37 @@ +using System.Threading; + +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + class NvMapHandle + { + public int Handle; + public int Id; + public int Size; + public int Align; + public int Kind; + public long Address; + public bool Allocated; + + private long Dupes; + + public NvMapHandle() + { + Dupes = 1; + } + + public NvMapHandle(int Size) : this() + { + this.Size = Size; + } + + public long IncrementRefCount() + { + return Interlocked.Increment(ref Dupes); + } + + public long DecrementRefCount() + { + return Interlocked.Decrement(ref Dupes); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandleParam.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandleParam.cs new file mode 100644 index 00000000..80ff4c03 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapHandleParam.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + enum NvMapHandleParam + { + Size = 1, + Align = 2, + Base = 3, + Heap = 4, + Kind = 5, + Compr = 6 + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapIoctl.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapIoctl.cs new file mode 100644 index 00000000..f9c1564a --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapIoctl.cs @@ -0,0 +1,302 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.Logging; +using Ryujinx.Core.OsHle.Utilities; +using Ryujinx.Core.Gpu; +using System.Collections.Concurrent; + +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + class NvMapIoctl + { + private const int FlagNotFreedYet = 1; + + private static ConcurrentDictionary<Process, IdDictionary> Maps; + + static NvMapIoctl() + { + Maps = new ConcurrentDictionary<Process, 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); + } + + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Unsupported Ioctl command 0x{Cmd:x8}!"); + + return NvResult.NotSupported; + } + + private static int Create(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvMapCreate Args = AMemoryHelper.Read<NvMapCreate>(Context.Memory, InputPosition); + + if (Args.Size == 0) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid size 0x{Args.Size:x8}!"); + + return NvResult.InvalidInput; + } + + int Size = IntUtils.RoundUp(Args.Size, NvGpuVmm.PageSize); + + Args.Handle = AddNvMap(Context, new NvMapHandle(Size)); + + Context.Ns.Log.PrintInfo(LogClass.ServiceNv, $"Created map {Args.Handle} with size 0x{Size:x8}!"); + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static int FromId(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvMapFromId Args = AMemoryHelper.Read<NvMapFromId>(Context.Memory, InputPosition); + + NvMapHandle Map = GetNvMap(Context, Args.Id); + + if (Map == null) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + + return NvResult.InvalidInput; + } + + Map.IncrementRefCount(); + + Args.Handle = Args.Id; + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static int Alloc(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvMapAlloc Args = AMemoryHelper.Read<NvMapAlloc>(Context.Memory, InputPosition); + + NvMapHandle Map = GetNvMap(Context, Args.Handle); + + if (Map == null) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + + return NvResult.InvalidInput; + } + + if ((Args.Align & (Args.Align - 1)) != 0) + { + Context.Ns.Log.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 = IntUtils.RoundUp(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. + if (!Context.Ns.Os.Allocator.TryAllocate((uint)Size, out Address)) + { + Result = NvResult.OutOfMemory; + } + } + + if (Result == NvResult.Success) + { + Map.Size = Size; + Map.Address = Address; + } + } + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return Result; + } + + private static int Free(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvMapFree Args = AMemoryHelper.Read<NvMapFree>(Context.Memory, InputPosition); + + NvMapHandle Map = GetNvMap(Context, Args.Handle); + + if (Map == null) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + + return NvResult.InvalidInput; + } + + long RefCount = Map.DecrementRefCount(); + + if (RefCount <= 0) + { + DeleteNvMap(Context, Args.Handle); + + Context.Ns.Log.PrintInfo(LogClass.ServiceNv, $"Deleted map {Args.Handle}!"); + + Args.Flags = 0; + } + else + { + Args.Flags = FlagNotFreedYet; + } + + Args.RefCount = RefCount; + Args.Size = Map.Size; + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static int Param(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvMapParam Args = AMemoryHelper.Read<NvMapParam>(Context.Memory, InputPosition); + + NvMapHandle Map = GetNvMap(Context, Args.Handle); + + if (Map == null) + { + Context.Ns.Log.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; + } + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static int GetId(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21Position(); + long OutputPosition = Context.Request.GetBufferType0x22Position(); + + NvMapGetId Args = AMemoryHelper.Read<NvMapGetId>(Context.Memory, InputPosition); + + NvMapHandle Map = GetNvMap(Context, Args.Handle); + + if (Map == null) + { + Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + + return NvResult.InvalidInput; + } + + Args.Id = Args.Handle; + + AMemoryHelper.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(Process Process) + { + Maps.TryRemove(Process, out _); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapParam.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapParam.cs new file mode 100644 index 00000000..196ef6ab --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap/NvMapParam.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Core.OsHle.Services.Nv.NvMap +{ + struct NvMapParam + { + public int Handle; + public int Param; + public int Result; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs deleted file mode 100644 index d8a47418..00000000 --- a/Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Ryujinx.Core.OsHle.Services.Nv -{ - class NvMapFb - { - private List<long> BufferOffs; - - public NvMapFb() - { - BufferOffs = new List<long>(); - } - - public void AddBufferOffset(long Offset) - { - BufferOffs.Add(Offset); - } - - public bool HasBufferOffset(int Index) - { - if ((uint)Index >= BufferOffs.Count) - { - return false; - } - - return true; - } - - public long GetBufferOffset(int Index) - { - if ((uint)Index >= BufferOffs.Count) - { - throw new ArgumentOutOfRangeException(nameof(Index)); - } - - return BufferOffs[Index]; - } - } -}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvResult.cs b/Ryujinx.Core/OsHle/Services/Nv/NvResult.cs new file mode 100644 index 00000000..5a419165 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvResult.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Core.OsHle.Services.Nv +{ + static class NvResult + { + public const int Success = 0; + public const int TryAgain = -11; + public const int OutOfMemory = -12; + public const int InvalidInput = -22; + public const int NotSupported = -25; + public const int Restart = -85; + public const int TimedOut = -110; + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs index 5aa3c3d5..db2f7fa2 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs @@ -1,9 +1,9 @@ using ChocolArm64.Memory; +using Ryujinx.Core.Gpu; using Ryujinx.Core.Logging; using Ryujinx.Core.OsHle.Handles; -using Ryujinx.Core.OsHle.Services.Nv; +using Ryujinx.Core.OsHle.Services.Nv.NvMap; using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Gpu; using System; using System.Collections.Generic; using System.IO; @@ -282,20 +282,12 @@ namespace Ryujinx.Core.OsHle.Services.Android int FbWidth = 1280; int FbHeight = 720; - NvMap Map = GetNvMap(Context, Slot); + int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c); + int BufferOffset = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x50); - NvMapFb MapFb = (NvMapFb)INvDrvServices.NvMapsFb.GetData(Context.Process, 0); + NvMapHandle Map = NvMapIoctl.GetNvMap(Context, NvMapHandle);; - long CpuAddr = Map.CpuAddress; - long GpuAddr = Map.GpuAddress; - - if (MapFb.HasBufferOffset(Slot)) - { - CpuAddr += MapFb.GetBufferOffset(Slot); - - //TODO: Enable once the frame buffers problems are fixed. - //GpuAddr += MapFb.GetBufferOffset(Slot); - } + long FbAddr = Map.Address + BufferOffset; BufferQueue[Slot].State = BufferState.Acquired; @@ -352,17 +344,17 @@ namespace Ryujinx.Core.OsHle.Services.Android //TODO: Support double buffering here aswell, it is broken for GPU //frame buffers because it seems to be completely out of sync. - if (Context.Ns.Gpu.Engine3d.IsFrameBufferPosition(GpuAddr)) + if (Context.Ns.Gpu.Engine3d.IsFrameBufferPosition(FbAddr)) { //Frame buffer is rendered to by the GPU, we can just //bind the frame buffer texture, it's not necessary to read anything. - Renderer.SetFrameBuffer(GpuAddr); + Renderer.SetFrameBuffer(FbAddr); } else { //Frame buffer is not set on the GPU registers, in this case //assume that the app is manually writing to it. - Texture Texture = new Texture(CpuAddr, FbWidth, FbHeight); + Texture Texture = new Texture(FbAddr, FbWidth, FbHeight); byte[] Data = TextureReader.Read(Context.Memory, Texture); @@ -372,22 +364,6 @@ namespace Ryujinx.Core.OsHle.Services.Android Context.Ns.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot)); } - private NvMap GetNvMap(ServiceCtx Context, int Slot) - { - int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c); - - if (!BitConverter.IsLittleEndian) - { - byte[] RawValue = BitConverter.GetBytes(NvMapHandle); - - Array.Reverse(RawValue); - - NvMapHandle = BitConverter.ToInt32(RawValue, 0); - } - - return INvDrvServices.NvMaps.GetData<NvMap>(Context.Process, NvMapHandle); - } - private void ReleaseBuffer(int Slot) { BufferQueue[Slot].State = BufferState.Free; diff --git a/Ryujinx.Core/OsHle/Utilities/IntUtils.cs b/Ryujinx.Core/OsHle/Utilities/IntUtils.cs new file mode 100644 index 00000000..4a522465 --- /dev/null +++ b/Ryujinx.Core/OsHle/Utilities/IntUtils.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Core.OsHle.Utilities +{ + static class IntUtils + { + public static int RoundUp(int Value, int Size) + { + return (Value + (Size - 1)) & ~(Size - 1); + } + + public static long RoundUp(long Value, int Size) + { + return (Value + (Size - 1)) & ~((long)Size - 1); + } + } +} diff --git a/Ryujinx.Core/OsHle/Utilities/MemReader.cs b/Ryujinx.Core/OsHle/Utilities/MemReader.cs deleted file mode 100644 index fe92f68f..00000000 --- a/Ryujinx.Core/OsHle/Utilities/MemReader.cs +++ /dev/null @@ -1,44 +0,0 @@ -using ChocolArm64.Memory; - -namespace Ryujinx.Core.OsHle.Utilities -{ - class MemReader - { - private AMemory Memory; - - public long Position { get; private set; } - - public MemReader(AMemory Memory, long Position) - { - this.Memory = Memory; - this.Position = Position; - } - - public byte ReadByte() - { - byte Value = Memory.ReadByte(Position); - - Position++; - - return Value; - } - - public int ReadInt32() - { - int Value = Memory.ReadInt32(Position); - - Position += 4; - - return Value; - } - - public long ReadInt64() - { - long Value = Memory.ReadInt64(Position); - - Position += 8; - - return Value; - } - } -}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Utilities/MemWriter.cs b/Ryujinx.Core/OsHle/Utilities/MemWriter.cs deleted file mode 100644 index 21b6a3b6..00000000 --- a/Ryujinx.Core/OsHle/Utilities/MemWriter.cs +++ /dev/null @@ -1,38 +0,0 @@ -using ChocolArm64.Memory; - -namespace Ryujinx.Core.OsHle.Utilities -{ - class MemWriter - { - private AMemory Memory; - - public long Position { get; private set; } - - public MemWriter(AMemory Memory, long Position) - { - this.Memory = Memory; - this.Position = Position; - } - - public void WriteByte(byte Value) - { - Memory.WriteByte(Position, Value); - - Position++; - } - - public void WriteInt32(int Value) - { - Memory.WriteInt32(Position, Value); - - Position += 4; - } - - public void WriteInt64(long Value) - { - Memory.WriteInt64(Position, Value); - - Position += 8; - } - } -}
\ No newline at end of file |
