From b9af34f3dd1e7f5e38b038f182c9fd4a791fdfea Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 18 Apr 2018 23:52:23 -0300 Subject: [HLE/Kernel] Somewhat improved sync primitives --- Ryujinx.Core/OsHle/AppletStateMgr.cs | 6 +- Ryujinx.Core/OsHle/CondVar.cs | 142 ---------- Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs | 154 ++++------- .../OsHle/Handles/KSynchronizationObject.cs | 6 +- Ryujinx.Core/OsHle/Handles/KThread.cs | 6 +- Ryujinx.Core/OsHle/Horizon.cs | 15 +- Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs | 139 ++++++++++ Ryujinx.Core/OsHle/Kernel/KernelErr.cs | 10 + Ryujinx.Core/OsHle/Kernel/MutualExclusion.cs | 91 ++++++ Ryujinx.Core/OsHle/Kernel/SvcHandler.cs | 123 +++++++++ Ryujinx.Core/OsHle/Kernel/SvcMemory.cs | 264 ++++++++++++++++++ Ryujinx.Core/OsHle/Kernel/SvcSystem.cs | 306 +++++++++++++++++++++ Ryujinx.Core/OsHle/Kernel/SvcThread.cs | 156 +++++++++++ Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs | 120 ++++++++ Ryujinx.Core/OsHle/KernelErr.cs | 10 - Ryujinx.Core/OsHle/Mutex.cs | 122 -------- Ryujinx.Core/OsHle/Process.cs | 39 ++- .../OsHle/Services/Aud/IAudioOutManager.cs | 2 +- Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs | 2 +- Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs | 2 +- Ryujinx.Core/OsHle/Svc/SvcHandler.cs | 116 -------- Ryujinx.Core/OsHle/Svc/SvcMemory.cs | 264 ------------------ Ryujinx.Core/OsHle/Svc/SvcSystem.cs | 306 --------------------- Ryujinx.Core/OsHle/Svc/SvcThread.cs | 146 ---------- Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs | 85 ------ Ryujinx/Ui/GLScreen.cs | 1 - 26 files changed, 1299 insertions(+), 1334 deletions(-) delete mode 100644 Ryujinx.Core/OsHle/CondVar.cs create mode 100644 Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs create mode 100644 Ryujinx.Core/OsHle/Kernel/KernelErr.cs create mode 100644 Ryujinx.Core/OsHle/Kernel/MutualExclusion.cs create mode 100644 Ryujinx.Core/OsHle/Kernel/SvcHandler.cs create mode 100644 Ryujinx.Core/OsHle/Kernel/SvcMemory.cs create mode 100644 Ryujinx.Core/OsHle/Kernel/SvcSystem.cs create mode 100644 Ryujinx.Core/OsHle/Kernel/SvcThread.cs create mode 100644 Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs delete mode 100644 Ryujinx.Core/OsHle/KernelErr.cs delete mode 100644 Ryujinx.Core/OsHle/Mutex.cs delete mode 100644 Ryujinx.Core/OsHle/Svc/SvcHandler.cs delete mode 100644 Ryujinx.Core/OsHle/Svc/SvcMemory.cs delete mode 100644 Ryujinx.Core/OsHle/Svc/SvcSystem.cs delete mode 100644 Ryujinx.Core/OsHle/Svc/SvcThread.cs delete mode 100644 Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs diff --git a/Ryujinx.Core/OsHle/AppletStateMgr.cs b/Ryujinx.Core/OsHle/AppletStateMgr.cs index 25f56c63..2199f43e 100644 --- a/Ryujinx.Core/OsHle/AppletStateMgr.cs +++ b/Ryujinx.Core/OsHle/AppletStateMgr.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Core.OsHle public void SetFocus(bool IsFocused) { - FocusState = IsFocused + FocusState = IsFocused ? FocusState.InFocus : FocusState.OutOfFocus; @@ -33,14 +33,14 @@ namespace Ryujinx.Core.OsHle { Messages.Enqueue(Message); - MessageEvent.Handle.Set(); + MessageEvent.WaitEvent.Set(); } public bool TryDequeueMessage(out MessageInfo Message) { if (Messages.Count < 2) { - MessageEvent.Handle.Reset(); + MessageEvent.WaitEvent.Reset(); } return Messages.TryDequeue(out Message); diff --git a/Ryujinx.Core/OsHle/CondVar.cs b/Ryujinx.Core/OsHle/CondVar.cs deleted file mode 100644 index f1b846d0..00000000 --- a/Ryujinx.Core/OsHle/CondVar.cs +++ /dev/null @@ -1,142 +0,0 @@ -using Ryujinx.Core.OsHle.Handles; -using System.Collections.Generic; -using System.Threading; - -namespace Ryujinx.Core.OsHle -{ - class CondVar - { - private Process Process; - - private long CondVarAddress; - private long Timeout; - - private bool OwnsCondVarValue; - - private List WaitingThreads; - - public CondVar(Process Process, long CondVarAddress, long Timeout) - { - this.Process = Process; - this.CondVarAddress = CondVarAddress; - this.Timeout = Timeout; - - WaitingThreads = new List(); - } - - public bool WaitForSignal(KThread Thread) - { - int Count = Process.Memory.ReadInt32(CondVarAddress); - - if (Count <= 0) - { - lock (WaitingThreads) - { - WaitingThreads.Add(Thread); - } - - if (Timeout == -1) - { - Process.Scheduler.WaitForSignal(Thread); - } - else - { - bool Result = Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000)); - - lock (WaitingThreads) - { - WaitingThreads.Remove(Thread); - } - - return Result; - } - } - - AcquireCondVarValue(); - - Count = Process.Memory.ReadInt32(CondVarAddress); - - if (Count > 0) - { - Process.Memory.WriteInt32(CondVarAddress, Count - 1); - } - - ReleaseCondVarValue(); - - return true; - } - - public void SetSignal(KThread Thread, int Count) - { - lock (WaitingThreads) - { - if (Count == -1) - { - Process.Scheduler.Signal(WaitingThreads.ToArray()); - - AcquireCondVarValue(); - - Process.Memory.WriteInt32(CondVarAddress, WaitingThreads.Count); - - ReleaseCondVarValue(); - - WaitingThreads.Clear(); - } - else - { - if (WaitingThreads.Count > 0) - { - int HighestPriority = WaitingThreads[0].Priority; - int HighestPrioIndex = 0; - - for (int Index = 1; Index < WaitingThreads.Count; Index++) - { - if (HighestPriority > WaitingThreads[Index].Priority) - { - HighestPriority = WaitingThreads[Index].Priority; - - HighestPrioIndex = Index; - } - } - - Process.Scheduler.Signal(WaitingThreads[HighestPrioIndex]); - - WaitingThreads.RemoveAt(HighestPrioIndex); - } - - AcquireCondVarValue(); - - Process.Memory.WriteInt32(CondVarAddress, Count); - - ReleaseCondVarValue(); - } - } - - Process.Scheduler.Suspend(Thread.ProcessorId); - Process.Scheduler.Resume(Thread); - } - - private void AcquireCondVarValue() - { - if (!OwnsCondVarValue) - { - while (!Process.Memory.AcquireAddress(CondVarAddress)) - { - Thread.Yield(); - } - - OwnsCondVarValue = true; - } - } - - private void ReleaseCondVarValue() - { - if (OwnsCondVarValue) - { - OwnsCondVarValue = false; - - Process.Memory.ReleaseAddress(CondVarAddress); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs index 7ba78b3f..238febd0 100644 --- a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs +++ b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs @@ -7,6 +7,8 @@ namespace Ryujinx.Core.OsHle.Handles { class KProcessScheduler : IDisposable { + private const int LowestPriority = 0x40; + private class SchedulerThread : IDisposable { public KThread Thread { get; private set; } @@ -51,7 +53,7 @@ namespace Ryujinx.Core.OsHle.Handles } } - public SchedulerThread Pop(int MinPriority = 0x40) + public SchedulerThread Pop(int MinPriority = LowestPriority) { lock (Threads) { @@ -130,68 +132,47 @@ namespace Ryujinx.Core.OsHle.Handles return; } - if (!ActiveProcessors.Contains(Thread.ProcessorId)) + if (ActiveProcessors.Add(Thread.ProcessorId)) { - ActiveProcessors.Add(Thread.ProcessorId); - Thread.Thread.Execute(); - Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} running."); + PrintDbgThreadInfo(Thread, "running."); } else { WaitingToRun[Thread.ProcessorId].Push(SchedThread); - Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run."); + PrintDbgThreadInfo(Thread, "waiting to run."); } } } - public void Suspend(int ProcessorId) + public void RemoveThread(KThread Thread) { + PrintDbgThreadInfo(Thread, "exited."); + lock (SchedLock) { - SchedulerThread SchedThread = WaitingToRun[ProcessorId].Pop(); + SchedulerThread NewThread = WaitingToRun[Thread.ProcessorId].Pop(); - if (SchedThread != null) - { - RunThread(SchedThread); - } - else + if (NewThread == null) { - ActiveProcessors.Remove(ProcessorId); - } - } - } + Logging.Debug(LogClass.KernelScheduler, $"Nothing to run on core {Thread.ProcessorId}!"); - public void Resume(KThread CurrThread) - { - SchedulerThread SchedThread; - - Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(CurrThread)} entering ipc delay wait state."); - - lock (SchedLock) - { - if (!AllThreads.TryGetValue(CurrThread, out SchedThread)) - { - Logging.Error(LogClass.KernelScheduler, $"{GetDbgThreadInfo(CurrThread)} was not found on the scheduler queue!"); + ActiveProcessors.Remove(Thread.ProcessorId); return; } - } - TryResumingExecution(SchedThread); + RunThread(NewThread); + } } - public bool WaitForSignal(KThread Thread, int Timeout = -1) + public void Suspend(int ProcessorId) { - SchedulerThread SchedThread; - - Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} entering signal wait state."); - lock (SchedLock) { - SchedThread = WaitingToRun[Thread.ProcessorId].Pop(); + SchedulerThread SchedThread = WaitingToRun[ProcessorId].Pop(); if (SchedThread != null) { @@ -199,88 +180,67 @@ namespace Ryujinx.Core.OsHle.Handles } else { - ActiveProcessors.Remove(Thread.ProcessorId); - } - - if (!AllThreads.TryGetValue(Thread, out SchedThread)) - { - Logging.Error(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!"); + Logging.Debug(LogClass.KernelScheduler, $"Nothing to run on core {ProcessorId}!"); - return false; + ActiveProcessors.Remove(ProcessorId); } } - - bool Result; - - if (Timeout >= 0) - { - Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms."); - - Result = SchedThread.WaitEvent.WaitOne(Timeout); - } - else - { - Result = SchedThread.WaitEvent.WaitOne(); - } - - TryResumingExecution(SchedThread); - - return Result; } - private void TryResumingExecution(SchedulerThread SchedThread) + public void Yield(KThread Thread) { - KThread Thread = SchedThread.Thread; + PrintDbgThreadInfo(Thread, "yielded execution."); lock (SchedLock) { - if (ActiveProcessors.Add(Thread.ProcessorId)) + SchedulerThread SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.Priority); + + if (SchedThread == null) { - Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} resuming execution..."); + PrintDbgThreadInfo(Thread, "resumed because theres nothing better to run."); return; } - WaitingToRun[Thread.ProcessorId].Push(SchedThread); + RunThread(SchedThread); } - SchedThread.WaitEvent.WaitOne(); - - Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} resuming execution..."); + Resume(Thread); } - public void Yield(KThread Thread) + public void Resume(KThread Thread) { SchedulerThread SchedThread; - Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} yielded execution."); - - lock (SchedLock) + if (!AllThreads.TryGetValue(Thread, out SchedThread)) { - SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.Priority); + throw new InvalidOperationException(); + } - if (SchedThread == null) - { - Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} resumed because theres nothing better to run."); + TryResumingExecution(SchedThread); + } - return; - } - - RunThread(SchedThread); + private void TryResumingExecution(SchedulerThread SchedThread) + { + KThread Thread = SchedThread.Thread; - if (!AllThreads.TryGetValue(Thread, out SchedThread)) + lock (SchedLock) + { + if (ActiveProcessors.Add(Thread.ProcessorId)) { - Logging.Error(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!"); + PrintDbgThreadInfo(Thread, "resuming execution..."); return; } + PrintDbgThreadInfo(Thread, "entering wait state..."); + WaitingToRun[Thread.ProcessorId].Push(SchedThread); } SchedThread.WaitEvent.WaitOne(); - Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} resuming execution..."); + PrintDbgThreadInfo(Thread, "resuming execution..."); } private void RunThread(SchedulerThread SchedThread) @@ -291,32 +251,16 @@ namespace Ryujinx.Core.OsHle.Handles } else { - Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(SchedThread.Thread)} running."); - } - } - - public void Signal(params KThread[] Threads) - { - lock (SchedLock) - { - foreach (KThread Thread in Threads) - { - if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) - { - if (!WaitingToRun[Thread.ProcessorId].HasThread(SchedThread)) - { - Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} signaled."); - - SchedThread.WaitEvent.Set(); - } - } - } + PrintDbgThreadInfo(SchedThread.Thread, "running."); } } - private string GetDbgThreadInfo(KThread Thread) + private void PrintDbgThreadInfo(KThread Thread, string Message) { - return $"Thread {Thread.ThreadId} (core {Thread.ProcessorId}) prio {Thread.Priority}"; + Logging.Debug(LogClass.KernelScheduler, "(" + + "ThreadId: " + Thread.ThreadId + ", " + + "ProcessorId: " + Thread.ProcessorId + ", " + + "Priority: " + Thread.Priority + ") " + Message); } public void Dispose() diff --git a/Ryujinx.Core/OsHle/Handles/KSynchronizationObject.cs b/Ryujinx.Core/OsHle/Handles/KSynchronizationObject.cs index 015b814a..3f78b965 100644 --- a/Ryujinx.Core/OsHle/Handles/KSynchronizationObject.cs +++ b/Ryujinx.Core/OsHle/Handles/KSynchronizationObject.cs @@ -5,11 +5,11 @@ namespace Ryujinx.Core.OsHle.Handles { class KSynchronizationObject : IDisposable { - public ManualResetEvent Handle { get; private set; } + public ManualResetEvent WaitEvent { get; private set; } public KSynchronizationObject() { - Handle = new ManualResetEvent(false); + WaitEvent = new ManualResetEvent(false); } public void Dispose() @@ -21,7 +21,7 @@ namespace Ryujinx.Core.OsHle.Handles { if (Disposing) { - Handle.Dispose(); + WaitEvent.Dispose(); } } } diff --git a/Ryujinx.Core/OsHle/Handles/KThread.cs b/Ryujinx.Core/OsHle/Handles/KThread.cs index aa1b27be..9742f492 100644 --- a/Ryujinx.Core/OsHle/Handles/KThread.cs +++ b/Ryujinx.Core/OsHle/Handles/KThread.cs @@ -6,8 +6,10 @@ namespace Ryujinx.Core.OsHle.Handles { public AThread Thread { get; private set; } - public int ProcessorId { get; private set; } - public int Priority { get; set; } + public int ProcessorId { get; private set; } + + public int Priority { get; set; } + public int Handle { get; set; } public int ThreadId => Thread.ThreadId; diff --git a/Ryujinx.Core/OsHle/Horizon.cs b/Ryujinx.Core/OsHle/Horizon.cs index 049b03b2..6442025f 100644 --- a/Ryujinx.Core/OsHle/Horizon.cs +++ b/Ryujinx.Core/OsHle/Horizon.cs @@ -11,8 +11,7 @@ namespace Ryujinx.Core.OsHle internal const int HidSize = 0x40000; internal const int FontSize = 0x50; - internal ConcurrentDictionary Mutexes { get; private set; } - internal ConcurrentDictionary CondVars { get; private set; } + private KProcessScheduler Scheduler; private ConcurrentDictionary Processes; @@ -27,8 +26,7 @@ namespace Ryujinx.Core.OsHle { this.Ns = Ns; - Mutexes = new ConcurrentDictionary(); - CondVars = new ConcurrentDictionary(); + Scheduler = new KProcessScheduler(); Processes = new ConcurrentDictionary(); @@ -95,7 +93,7 @@ namespace Ryujinx.Core.OsHle MainProcess.Run(IsNro); } - public void SignalVsync() => VsyncEvent.Handle.Set(); + public void SignalVsync() => VsyncEvent.WaitEvent.Set(); private Process MakeProcess() { @@ -110,7 +108,7 @@ namespace Ryujinx.Core.OsHle ProcessId++; } - Process = new Process(Ns, ProcessId); + Process = new Process(Ns, Scheduler, ProcessId); Processes.TryAdd(ProcessId, Process); } @@ -144,11 +142,6 @@ namespace Ryujinx.Core.OsHle if (File.Exists(NextNro)) { - //TODO: Those dictionaries shouldn't even exist, - //the Mutex and CondVar helper classes should be static. - Mutexes.Clear(); - CondVars.Clear(); - LoadProgram(NextNro); } } diff --git a/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs b/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs new file mode 100644 index 00000000..34d5820b --- /dev/null +++ b/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs @@ -0,0 +1,139 @@ +using Ryujinx.Core.OsHle.Handles; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.Core.OsHle.Kernel +{ + class ConditionVariable + { + private Process Process; + + private long CondVarAddress; + + private bool OwnsCondVarValue; + + private List<(KThread Thread, AutoResetEvent WaitEvent)> WaitingThreads; + + public ConditionVariable(Process Process, long CondVarAddress) + { + this.Process = Process; + this.CondVarAddress = CondVarAddress; + + WaitingThreads = new List<(KThread, AutoResetEvent)>(); + } + + public bool WaitForSignal(KThread Thread, long Timeout) + { + bool Result = true; + + int Count = Process.Memory.ReadInt32(CondVarAddress); + + if (Count <= 0) + { + using (AutoResetEvent WaitEvent = new AutoResetEvent(false)) + { + lock (WaitingThreads) + { + WaitingThreads.Add((Thread, WaitEvent)); + } + + Process.Scheduler.Suspend(Thread.ProcessorId); + + if (Timeout < 0) + { + Result = WaitEvent.WaitOne(); + } + else + { + Result = WaitEvent.WaitOne((int)(Timeout / 1000000)); + + lock (WaitingThreads) + { + WaitingThreads.Remove((Thread, WaitEvent)); + } + } + + Process.Scheduler.Resume(Thread); + } + } + + AcquireCondVarValue(); + + Count = Process.Memory.ReadInt32(CondVarAddress); + + if (Count > 0) + { + Process.Memory.WriteInt32(CondVarAddress, Count - 1); + } + + ReleaseCondVarValue(); + + return Result; + } + + public void SetSignal(KThread Thread, int Count) + { + lock (WaitingThreads) + { + if (Count < 0) + { + Process.Memory.WriteInt32(CondVarAddress, WaitingThreads.Count); + + foreach ((_, AutoResetEvent WaitEvent) in WaitingThreads) + { + WaitEvent.Set(); + } + + WaitingThreads.Clear(); + } + else + { + Process.Memory.WriteInt32(CondVarAddress, Count); + + while (WaitingThreads.Count > 0 && Count-- > 0) + { + int HighestPriority = WaitingThreads[0].Thread.Priority; + int HighestPrioIndex = 0; + + for (int Index = 1; Index < WaitingThreads.Count; Index++) + { + if (HighestPriority > WaitingThreads[Index].Thread.Priority) + { + HighestPriority = WaitingThreads[Index].Thread.Priority; + + HighestPrioIndex = Index; + } + } + + WaitingThreads[HighestPrioIndex].WaitEvent.Set(); + + WaitingThreads.RemoveAt(HighestPrioIndex); + } + } + } + } + + private void AcquireCondVarValue() + { + if (!OwnsCondVarValue) + { + while (!Process.Memory.AcquireAddress(CondVarAddress)) + { + Thread.Yield(); + } + + OwnsCondVarValue = true; + } + } + + private void ReleaseCondVarValue() + { + if (OwnsCondVarValue) + { + OwnsCondVarValue = false; + + Process.Memory.ReleaseAddress(CondVarAddress); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Kernel/KernelErr.cs b/Ryujinx.Core/OsHle/Kernel/KernelErr.cs new file mode 100644 index 00000000..e7cd72dc --- /dev/null +++ b/Ryujinx.Core/OsHle/Kernel/KernelErr.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Core.OsHle.Kernel +{ + static class KernelErr + { + public const int InvalidMemRange = 110; + public const int InvalidHandle = 114; + public const int Timeout = 117; + public const int InvalidInfo = 120; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Kernel/MutualExclusion.cs b/Ryujinx.Core/OsHle/Kernel/MutualExclusion.cs new file mode 100644 index 00000000..aeaaf70f --- /dev/null +++ b/Ryujinx.Core/OsHle/Kernel/MutualExclusion.cs @@ -0,0 +1,91 @@ +using Ryujinx.Core.OsHle.Handles; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.Core.OsHle.Kernel +{ + class MutualExclusion + { + private const int MutexHasListenersMask = 0x40000000; + + private Process Process; + + private long MutexAddress; + + private List<(KThread Thread, AutoResetEvent WaitEvent)> WaitingThreads; + + public MutualExclusion(Process Process, long MutexAddress) + { + this.Process = Process; + this.MutexAddress = MutexAddress; + + WaitingThreads = new List<(KThread, AutoResetEvent)>(); + } + + public void WaitForLock(KThread RequestingThread) + { + int OwnerThreadHandle = Process.Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask; + + WaitForLock(RequestingThread, OwnerThreadHandle); + } + + public void WaitForLock(KThread RequestingThread, int OwnerThreadHandle) + { + if (OwnerThreadHandle == RequestingThread.Handle || + OwnerThreadHandle == 0) + { + return; + } + + using (AutoResetEvent WaitEvent = new AutoResetEvent(false)) + { + lock (WaitingThreads) + { + WaitingThreads.Add((RequestingThread, WaitEvent)); + } + + Process.Scheduler.Suspend(RequestingThread.ProcessorId); + + WaitEvent.WaitOne(); + + Process.Scheduler.Resume(RequestingThread); + } + } + + public void Unlock() + { + lock (WaitingThreads) + { + int HasListeners = WaitingThreads.Count > 1 ? MutexHasListenersMask : 0; + + if (WaitingThreads.Count > 0) + { + int HighestPriority = WaitingThreads[0].Thread.Priority; + int HighestPrioIndex = 0; + + for (int Index = 1; Index < WaitingThreads.Count; Index++) + { + if (HighestPriority > WaitingThreads[Index].Thread.Priority) + { + HighestPriority = WaitingThreads[Index].Thread.Priority; + + HighestPrioIndex = Index; + } + } + + int Handle = WaitingThreads[HighestPrioIndex].Thread.Handle; + + WaitingThreads[HighestPrioIndex].WaitEvent.Set(); + + WaitingThreads.RemoveAt(HighestPrioIndex); + + Process.Memory.WriteInt32(MutexAddress, HasListeners | Handle); + } + else + { + Process.Memory.WriteInt32(MutexAddress, 0); + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs new file mode 100644 index 00000000..4d93ef29 --- /dev/null +++ b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs @@ -0,0 +1,123 @@ +using ChocolArm64.Events; +using ChocolArm64.Memory; +using ChocolArm64.State; +using Ryujinx.Core.OsHle.Handles; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.Kernel +{ + partial class SvcHandler : IDisposable + { + private delegate void SvcFunc(AThreadState ThreadState); + + private Dictionary SvcFuncs; + + private Switch Ns; + private Process Process; + private AMemory Memory; + + private ConcurrentDictionary Mutexes; + private ConcurrentDictionary CondVars; + + private HashSet<(HSharedMem, long)> MappedSharedMems; + + private ulong CurrentHeapSize; + + private static Random Rng; + + public SvcHandler(Switch Ns, Process Process) + { + SvcFuncs = new Dictionary() + { + { 0x01, SvcSetHeapSize }, + { 0x03, SvcSetMemoryAttribute }, + { 0x04, SvcMapMemory }, + { 0x05, SvcUnmapMemory }, + { 0x06, SvcQueryMemory }, + { 0x07, SvcExitProcess }, + { 0x08, SvcCreateThread }, + { 0x09, SvcStartThread }, + { 0x0a, SvcExitThread }, + { 0x0b, SvcSleepThread }, + { 0x0c, SvcGetThreadPriority }, + { 0x0d, SvcSetThreadPriority }, + { 0x0f, SvcSetThreadCoreMask }, + { 0x10, SvcGetCurrentProcessorNumber }, + { 0x12, SvcClearEvent }, + { 0x13, SvcMapSharedMemory }, + { 0x14, SvcUnmapSharedMemory }, + { 0x15, SvcCreateTransferMemory }, + { 0x16, SvcCloseHandle }, + { 0x17, SvcResetSignal }, + { 0x18, SvcWaitSynchronization }, + { 0x1a, SvcArbitrateLock }, + { 0x1b, SvcArbitrateUnlock }, + { 0x1c, SvcWaitProcessWideKeyAtomic }, + { 0x1d, SvcSignalProcessWideKey }, + { 0x1e, SvcGetSystemTick }, + { 0x1f, SvcConnectToNamedPort }, + { 0x21, SvcSendSyncRequest }, + { 0x22, SvcSendSyncRequestWithUserBuffer }, + { 0x25, SvcGetThreadId }, + { 0x26, SvcBreak }, + { 0x27, SvcOutputDebugString }, + { 0x29, SvcGetInfo } + }; + + this.Ns = Ns; + this.Process = Process; + this.Memory = Process.Memory; + + Mutexes = new ConcurrentDictionary(); + CondVars = new ConcurrentDictionary(); + + MappedSharedMems = new HashSet<(HSharedMem, long)>(); + } + + static SvcHandler() + { + Rng = new Random(); + } + + public void SvcCall(object sender, AInstExceptionEventArgs e) + { + AThreadState ThreadState = (AThreadState)sender; + + if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func)) + { + Logging.Trace(LogClass.KernelSvc, $"(Thread {ThreadState.ThreadId}) {Func.Method.Name} called."); + + Func(ThreadState); + + Logging.Trace(LogClass.KernelSvc, $"(Thread {ThreadState.ThreadId}) {Func.Method.Name} ended."); + } + else + { + throw new NotImplementedException(e.Id.ToString("x4")); + } + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + lock (MappedSharedMems) + { + foreach ((HSharedMem SharedMem, long Position) in MappedSharedMems) + { + SharedMem.RemoveVirtualPosition(Memory, Position); + } + + MappedSharedMems.Clear(); + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Kernel/SvcMemory.cs b/Ryujinx.Core/OsHle/Kernel/SvcMemory.cs new file mode 100644 index 00000000..c8aedcff --- /dev/null +++ b/Ryujinx.Core/OsHle/Kernel/SvcMemory.cs @@ -0,0 +1,264 @@ +using ChocolArm64.Memory; +using ChocolArm64.State; +using Ryujinx.Core.OsHle.Handles; + +using static Ryujinx.Core.OsHle.ErrorCode; + +namespace Ryujinx.Core.OsHle.Kernel +{ + partial class SvcHandler + { + private void SvcSetHeapSize(AThreadState ThreadState) + { + uint Size = (uint)ThreadState.X1; + + long Position = MemoryRegions.HeapRegionAddress; + + if (Size > CurrentHeapSize) + { + Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW); + } + else + { + Memory.Manager.Unmap(Position + Size, (long)CurrentHeapSize - Size); + } + + CurrentHeapSize = Size; + + ThreadState.X0 = 0; + ThreadState.X1 = (ulong)Position; + } + + private void SvcSetMemoryAttribute(AThreadState ThreadState) + { + long Position = (long)ThreadState.X0; + long Size = (long)ThreadState.X1; + int State0 = (int)ThreadState.X2; + int State1 = (int)ThreadState.X3; + + if ((State0 == 0 && State1 == 0) || + (State0 == 8 && State1 == 0)) + { + Memory.Manager.ClearAttrBit(Position, Size, 3); + } + else if (State0 == 8 && State1 == 8) + { + Memory.Manager.SetAttrBit(Position, Size, 3); + } + + ThreadState.X0 = 0; + } + + private void SvcMapMemory(AThreadState ThreadState) + { + long Dst = (long)ThreadState.X0; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + + if (!IsValidPosition(Src)) + { + Logging.Warn(LogClass.KernelSvc, $"Tried to map Memory at invalid src address {Src:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + if (!IsValidMapPosition(Dst)) + { + Logging.Warn(LogClass.KernelSvc, $"Tried to map Memory at invalid dst address {Dst:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src); + + Memory.Manager.Map(Dst, Size, (int)MemoryType.MappedMemory, SrcInfo.Perm); + + Memory.Manager.Reprotect(Src, Size, AMemoryPerm.None); + + Memory.Manager.SetAttrBit(Src, Size, 0); + + ThreadState.X0 = 0; + } + + private void SvcUnmapMemory(AThreadState ThreadState) + { + long Dst = (long)ThreadState.X0; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + + if (!IsValidPosition(Src)) + { + Logging.Warn(LogClass.KernelSvc, $"Tried to unmap Memory at invalid src address {Src:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + if (!IsValidMapPosition(Dst)) + { + Logging.Warn(LogClass.KernelSvc, $"Tried to unmap Memory at invalid dst address {Dst:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst); + + Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory); + + Memory.Manager.Reprotect(Src, Size, DstInfo.Perm); + + Memory.Manager.ClearAttrBit(Src, Size, 0); + + ThreadState.X0 = 0; + } + + private void SvcQueryMemory(AThreadState ThreadState) + { + long InfoPtr = (long)ThreadState.X0; + long Position = (long)ThreadState.X2; + + AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position); + + if (MapInfo == null) + { + long AddrSpaceEnd = MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize; + + long ReservedSize = (long)(ulong.MaxValue - (ulong)AddrSpaceEnd) + 1; + + MapInfo = new AMemoryMapInfo(AddrSpaceEnd, ReservedSize, (int)MemoryType.Reserved, 0, AMemoryPerm.None); + } + + Memory.WriteInt64(InfoPtr + 0x00, MapInfo.Position); + Memory.WriteInt64(InfoPtr + 0x08, MapInfo.Size); + Memory.WriteInt32(InfoPtr + 0x10, MapInfo.Type); + Memory.WriteInt32(InfoPtr + 0x14, MapInfo.Attr); + Memory.WriteInt32(InfoPtr + 0x18, (int)MapInfo.Perm); + Memory.WriteInt32(InfoPtr + 0x1c, 0); + Memory.WriteInt32(InfoPtr + 0x20, 0); + Memory.WriteInt32(InfoPtr + 0x24, 0); + //TODO: X1. + + ThreadState.X0 = 0; + ThreadState.X1 = 0; + } + + private void SvcMapSharedMemory(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X0; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + int Perm = (int)ThreadState.X3; + + if (!IsValidPosition(Src)) + { + Logging.Warn(LogClass.KernelSvc, $"Tried to map SharedMemory at invalid address {Src:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + HSharedMem SharedMem = Process.HandleTable.GetData(Handle); + + if (SharedMem != null) + { + Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, AMemoryPerm.Write); + + AMemoryHelper.FillWithZeros(Memory, Src, (int)Size); + + Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); + + lock (MappedSharedMems) + { + MappedSharedMems.Add((SharedMem, Src)); + } + + SharedMem.AddVirtualPosition(Memory, Src); + + ThreadState.X0 = 0; + } + + //TODO: Error codes. + } + + private void SvcUnmapSharedMemory(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X0; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + + if (!IsValidPosition(Src)) + { + Logging.Warn(LogClass.KernelSvc, $"Tried to unmap SharedMemory at invalid address {Src:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + HSharedMem SharedMem = Process.HandleTable.GetData(Handle); + + if (SharedMem != null) + { + Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory); + + SharedMem.RemoveVirtualPosition(Memory, Src); + + lock (MappedSharedMems) + { + MappedSharedMems.Remove((SharedMem, Src)); + } + + ThreadState.X0 = 0; + } + + //TODO: Error codes. + } + + private void SvcCreateTransferMemory(AThreadState ThreadState) + { + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + int Perm = (int)ThreadState.X3; + + if (!IsValidPosition(Src)) + { + Logging.Warn(LogClass.KernelSvc, $"Tried to create TransferMemory at invalid address {Src:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Src); + + Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); + + HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size); + + ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem); + + ThreadState.X0 = 0; + ThreadState.X1 = Handle; + } + + private static bool IsValidPosition(long Position) + { + return Position >= MemoryRegions.AddrSpaceStart && + Position < MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize; + } + + private static bool IsValidMapPosition(long Position) + { + return Position >= MemoryRegions.MapRegionAddress && + Position < MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Kernel/SvcSystem.cs b/Ryujinx.Core/OsHle/Kernel/SvcSystem.cs new file mode 100644 index 00000000..ebbbef4a --- /dev/null +++ b/Ryujinx.Core/OsHle/Kernel/SvcSystem.cs @@ -0,0 +1,306 @@ +using ChocolArm64.Memory; +using ChocolArm64.State; +using Ryujinx.Core.OsHle.Exceptions; +using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.Ipc; +using Ryujinx.Core.OsHle.Services; +using System; +using System.Threading; + +using static Ryujinx.Core.OsHle.ErrorCode; + +namespace Ryujinx.Core.OsHle.Kernel +{ + partial class SvcHandler + { + private const int AllowedCpuIdBitmask = 0b1111; + + private const bool EnableProcessDebugging = false; + + private void SvcExitProcess(AThreadState ThreadState) + { + Ns.Os.ExitProcess(ThreadState.ProcessId); + } + + private void SvcClearEvent(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X0; + + //TODO: Implement events. + + ThreadState.X0 = 0; + } + + private void SvcCloseHandle(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X0; + + object Obj = Process.HandleTable.CloseHandle(Handle); + + if (Obj == null) + { + Logging.Warn(LogClass.KernelSvc, $"Tried to CloseHandle on invalid handle 0x{Handle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + return; + } + + if (Obj is KSession Session) + { + Session.Dispose(); + } + else if (Obj is HTransferMem TMem) + { + TMem.Memory.Manager.Reprotect( + TMem.Position, + TMem.Size, + TMem.Perm); + } + + ThreadState.X0 = 0; + } + + private void SvcResetSignal(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X0; + + KEvent Event = Process.HandleTable.GetData(Handle); + + if (Event != null) + { + Event.WaitEvent.Reset(); + + ThreadState.X0 = 0; + } + else + { + Logging.Warn(LogClass.KernelSvc, $"Tried to ResetSignal on invalid event handle 0x{Handle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + } + } + + private void SvcWaitSynchronization(AThreadState ThreadState) + { + long HandlesPtr = (long)ThreadState.X1; + int HandlesCount = (int)ThreadState.X2; + long Timeout = (long)ThreadState.X3; + + KThread CurrThread = Process.GetThread(ThreadState.Tpidr); + + WaitHandle[] Handles = new WaitHandle[HandlesCount]; + + for (int Index = 0; Index < HandlesCount; Index++) + { + int Handle = Memory.ReadInt32(HandlesPtr + Index * 4); + + KSynchronizationObject SyncObj = Process.HandleTable.GetData(Handle); + + if (SyncObj == null) + { + Logging.Warn(LogClass.KernelSvc, $"Tried to WaitSynchronization on invalid handle 0x{Handle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + return; + } + + Handles[Index] = SyncObj.WaitEvent; + } + + Process.Scheduler.Suspend(CurrThread.ProcessorId); + + int HandleIndex; + + ulong Result = 0; + + if (Timeout != -1) + { + HandleIndex = WaitHandle.WaitAny(Handles, (int)(Timeout / 1000000)); + + if (HandleIndex == WaitHandle.WaitTimeout) + { + Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout); + } + } + else + { + HandleIndex = WaitHandle.WaitAny(Handles); + } + + Process.Scheduler.Resume(CurrThread); + + ThreadState.X0 = Result; + + if (Result == 0) + { + ThreadState.X1 = (ulong)HandleIndex; + } + } + + private void SvcGetSystemTick(AThreadState ThreadState) + { + ThreadState.X0 = ThreadState.CntpctEl0; + } + + private void SvcConnectToNamedPort(AThreadState ThreadState) + { + long StackPtr = (long)ThreadState.X0; + long NamePtr = (long)ThreadState.X1; + + string Name = AMemoryHelper.ReadAsciiString(Memory, NamePtr, 8); + + //TODO: Validate that app has perms to access the service, and that the service + //actually exists, return error codes otherwise. + KSession Session = new KSession(ServiceFactory.MakeService(Name), Name); + + ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session); + + ThreadState.X0 = 0; + ThreadState.X1 = Handle; + } + + private void SvcSendSyncRequest(AThreadState ThreadState) + { + SendSyncRequest(ThreadState, ThreadState.Tpidr, 0x100, (int)ThreadState.X0); + } + + private void SvcSendSyncRequestWithUserBuffer(AThreadState ThreadState) + { + SendSyncRequest( + ThreadState, + (long)ThreadState.X0, + (long)ThreadState.X1, + (int)ThreadState.X2); + } + + private void SendSyncRequest(AThreadState ThreadState, long CmdPtr, long Size, int Handle) + { + KThread CurrThread = Process.GetThread(ThreadState.Tpidr); + + byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, Size); + + KSession Session = Process.HandleTable.GetData(Handle); + + if (Session != null) + { + Process.Scheduler.Suspend(CurrThread.ProcessorId); + + IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr); + + IpcHandler.IpcCall(Ns, Process, Memory, Session, Cmd, CmdPtr); + + Thread.Yield(); + + Process.Scheduler.Resume(CurrThread); + + ThreadState.X0 = 0; + } + else + { + Logging.Warn(LogClass.KernelSvc, $"Tried to SendSyncRequest on invalid session handle 0x{Handle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + } + } + + private void SvcBreak(AThreadState ThreadState) + { + long Reason = (long)ThreadState.X0; + long Unknown = (long)ThreadState.X1; + long Info = (long)ThreadState.X2; + + throw new GuestBrokeExecutionException(); + } + + private void SvcOutputDebugString(AThreadState ThreadState) + { + long Position = (long)ThreadState.X0; + long Size = (long)ThreadState.X1; + + string Str = AMemoryHelper.ReadAsciiString(Memory, Position, Size); + + Logging.Info(LogClass.KernelSvc, Str); + + ThreadState.X0 = 0; + } + + private void SvcGetInfo(AThreadState ThreadState) + { + long StackPtr = (long)ThreadState.X0; + int InfoType = (int)ThreadState.X1; + long Handle = (long)ThreadState.X2; + int InfoId = (int)ThreadState.X3; + + //Fail for info not available on older Kernel versions. + if (InfoType == 18 || + InfoType == 19 || + InfoType == 20) + { + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo); + + return; + } + + switch (InfoType) + { + case 0: + ThreadState.X1 = AllowedCpuIdBitmask; + break; + + case 2: + ThreadState.X1 = MemoryRegions.MapRegionAddress; + break; + + case 3: + ThreadState.X1 = MemoryRegions.MapRegionSize; + break; + + case 4: + ThreadState.X1 = MemoryRegions.HeapRegionAddress; + break; + + case 5: + ThreadState.X1 = MemoryRegions.HeapRegionSize; + break; + + case 6: + ThreadState.X1 = MemoryRegions.TotalMemoryAvailable; + break; + + case 7: + ThreadState.X1 = MemoryRegions.TotalMemoryUsed + CurrentHeapSize; + break; + + case 8: + ThreadState.X1 = EnableProcessDebugging ? 1 : 0; + break; + + case 11: + ThreadState.X1 = (ulong)Rng.Next() + ((ulong)Rng.Next() << 32); + break; + + case 12: + ThreadState.X1 = MemoryRegions.AddrSpaceStart; + break; + + case 13: + ThreadState.X1 = MemoryRegions.AddrSpaceSize; + break; + + case 14: + ThreadState.X1 = MemoryRegions.MapRegionAddress; + break; + + case 15: + ThreadState.X1 = MemoryRegions.MapRegionSize; + break; + + default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}"); + } + + ThreadState.X0 = 0; + } + } +} diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs new file mode 100644 index 00000000..7418732f --- /dev/null +++ b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs @@ -0,0 +1,156 @@ +using ChocolArm64.State; +using Ryujinx.Core.OsHle.Handles; +using System.Threading; + +using static Ryujinx.Core.OsHle.ErrorCode; + +namespace Ryujinx.Core.OsHle.Kernel +{ + partial class SvcHandler + { + private void SvcCreateThread(AThreadState ThreadState) + { + long EntryPoint = (long)ThreadState.X1; + long ArgsPtr = (long)ThreadState.X2; + long StackTop = (long)ThreadState.X3; + int Priority = (int)ThreadState.X4; + int ProcessorId = (int)ThreadState.X5; + + if (ProcessorId == -2) + { + //TODO: Get this value from the NPDM file. + ProcessorId = 0; + } + + int Handle = Process.MakeThread( + EntryPoint, + StackTop, + ArgsPtr, + Priority, + ProcessorId); + + ThreadState.X0 = 0; + ThreadState.X1 = (ulong)Handle; + } + + private void SvcStartThread(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X0; + + KThread CurrThread = Process.HandleTable.GetData(Handle); + + if (CurrThread != null) + { + Process.Scheduler.StartThread(CurrThread); + + Process.Scheduler.Yield(Process.GetThread(ThreadState.Tpidr)); + + ThreadState.X0 = 0; + } + else + { + Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + } + } + + private void SvcExitThread(AThreadState ThreadState) + { + KThread CurrThread = Process.GetThread(ThreadState.Tpidr); + + CurrThread.Thread.StopExecution(); + } + + private void SvcSleepThread(AThreadState ThreadState) + { + ulong NanoSecs = ThreadState.X0; + + KThread CurrThread = Process.GetThread(ThreadState.Tpidr); + + if (NanoSecs == 0) + { + Process.Scheduler.Yield(CurrThread); + } + else + { + Process.Scheduler.Suspend(CurrThread.ProcessorId); + + Thread.Sleep((int)(NanoSecs / 1000000)); + + Process.Scheduler.Resume(CurrThread); + } + } + + private void SvcGetThreadPriority(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X1; + + KThread CurrThread = Process.HandleTable.GetData(Handle); + + if (CurrThread != null) + { + ThreadState.X0 = 0; + ThreadState.X1 = (ulong)CurrThread.Priority; + } + else + { + Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + } + } + + private void SvcSetThreadPriority(AThreadState ThreadState) + { + int Prio = (int)ThreadState.X0; + int Handle = (int)ThreadState.X1; + + KThread CurrThread = Process.HandleTable.GetData(Handle); + + if (CurrThread != null) + { + CurrThread.Priority = Prio; + + ThreadState.X0 = 0; + } + else + { + Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + } + } + + private void SvcSetThreadCoreMask(AThreadState ThreadState) + { + ThreadState.X0 = 0; + + //TODO: Error codes. + } + + private void SvcGetCurrentProcessorNumber(AThreadState ThreadState) + { + ThreadState.X0 = (ulong)Process.GetThread(ThreadState.Tpidr).ProcessorId; + } + + private void SvcGetThreadId(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X1; + + KThread CurrThread = Process.HandleTable.GetData(Handle); + + if (CurrThread != null) + { + ThreadState.X0 = 0; + ThreadState.X1 = (ulong)CurrThread.ThreadId; + } + else + { + Logging.Warn(LogClass.KernelSvc, $"Tried to GetThreadId on invalid thread handle 0x{Handle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs b/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs new file mode 100644 index 00000000..e9d801b4 --- /dev/null +++ b/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs @@ -0,0 +1,120 @@ +using ChocolArm64.State; +using Ryujinx.Core.OsHle.Handles; + +using static Ryujinx.Core.OsHle.ErrorCode; + +namespace Ryujinx.Core.OsHle.Kernel +{ + partial class SvcHandler + { + private void SvcArbitrateLock(AThreadState ThreadState) + { + int OwnerThreadHandle = (int)ThreadState.X0; + long MutexAddress = (long)ThreadState.X1; + int RequestingThreadHandle = (int)ThreadState.X2; + + KThread OwnerThread = Process.HandleTable.GetData(OwnerThreadHandle); + + if (OwnerThread == null) + { + Logging.Warn(LogClass.KernelSvc, $"Invalid owner thread handle 0x{OwnerThreadHandle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + return; + } + + KThread RequestingThread = Process.HandleTable.GetData(RequestingThreadHandle); + + if (RequestingThread == null) + { + Logging.Warn(LogClass.KernelSvc, $"Invalid requesting thread handle 0x{RequestingThreadHandle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + return; + } + + MutualExclusion Mutex = GetMutex(MutexAddress); + + Mutex.WaitForLock(RequestingThread, OwnerThreadHandle); + + ThreadState.X0 = 0; + } + + private void SvcArbitrateUnlock(AThreadState ThreadState) + { + long MutexAddress = (long)ThreadState.X0; + + GetMutex(MutexAddress).Unlock(); + + Process.Scheduler.Yield(Process.GetThread(ThreadState.Tpidr)); + + ThreadState.X0 = 0; + } + + private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState) + { + long MutexAddress = (long)ThreadState.X0; + long CondVarAddress = (long)ThreadState.X1; + int ThreadHandle = (int)ThreadState.X2; + long Timeout = (long)ThreadState.X3; + + KThread Thread = Process.HandleTable.GetData(ThreadHandle); + + if (Thread == null) + { + Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + } + + MutualExclusion Mutex = GetMutex(MutexAddress); + + Mutex.Unlock(); + + if (!GetCondVar(CondVarAddress).WaitForSignal(Thread, Timeout)) + { + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.Timeout); + + return; + } + + Mutex.WaitForLock(Thread); + + ThreadState.X0 = 0; + } + + private void SvcSignalProcessWideKey(AThreadState ThreadState) + { + long CondVarAddress = (long)ThreadState.X0; + int Count = (int)ThreadState.X1; + + KThread CurrThread = Process.GetThread(ThreadState.Tpidr); + + GetCondVar(CondVarAddress).SetSignal(CurrThread, Count); + + ThreadState.X0 = 0; + } + + private MutualExclusion GetMutex(long MutexAddress) + { + MutualExclusion MutexFactory(long Key) + { + return new MutualExclusion(Process, MutexAddress); + } + + return Mutexes.GetOrAdd(MutexAddress, MutexFactory); + } + + private ConditionVariable GetCondVar(long CondVarAddress) + { + ConditionVariable CondVarFactory(long Key) + { + return new ConditionVariable(Process, CondVarAddress); + } + + return CondVars.GetOrAdd(CondVarAddress, CondVarFactory); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/KernelErr.cs b/Ryujinx.Core/OsHle/KernelErr.cs deleted file mode 100644 index e476f631..00000000 --- a/Ryujinx.Core/OsHle/KernelErr.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.Core.OsHle -{ - static class KernelErr - { - public const int InvalidMemRange = 110; - public const int InvalidHandle = 114; - public const int Timeout = 117; - public const int InvalidInfo = 120; - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Mutex.cs b/Ryujinx.Core/OsHle/Mutex.cs deleted file mode 100644 index 7a0e8b6c..00000000 --- a/Ryujinx.Core/OsHle/Mutex.cs +++ /dev/null @@ -1,122 +0,0 @@ -using Ryujinx.Core.OsHle.Handles; -using System.Collections.Concurrent; -using System.Threading; - -namespace Ryujinx.Core.OsHle -{ - class Mutex - { - private const int MutexHasListenersMask = 0x40000000; - - private Process Process; - - private long MutexAddress; - - private bool OwnsMutexValue; - - private object EnterWaitLock; - - private ConcurrentQueue WaitingThreads; - - public Mutex(Process Process, long MutexAddress, int OwnerThreadHandle) - { - this.Process = Process; - this.MutexAddress = MutexAddress; - - //Process.Memory.WriteInt32(MutexAddress, OwnerThreadHandle); - - EnterWaitLock = new object(); - - WaitingThreads = new ConcurrentQueue(); - } - - public void WaitForLock(KThread RequestingThread, int RequestingThreadHandle) - { - AcquireMutexValue(); - - lock (EnterWaitLock) - { - int CurrentThreadHandle = Process.Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask; - - if (CurrentThreadHandle == RequestingThreadHandle || - CurrentThreadHandle == 0) - { - return; - } - - Process.Memory.WriteInt32(MutexAddress, CurrentThreadHandle | MutexHasListenersMask); - - ReleaseMutexValue(); - - WaitingThreads.Enqueue(RequestingThread); - } - - Process.Scheduler.WaitForSignal(RequestingThread); - } - - public void GiveUpLock(int ThreadHandle) - { - AcquireMutexValue(); - - lock (EnterWaitLock) - { - int CurrentThread = Process.Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask; - - if (CurrentThread == ThreadHandle) - { - Unlock(); - } - } - - ReleaseMutexValue(); - } - - public void Unlock() - { - AcquireMutexValue(); - - lock (EnterWaitLock) - { - int HasListeners = WaitingThreads.Count > 1 ? MutexHasListenersMask : 0; - - Process.Memory.WriteInt32(MutexAddress, HasListeners); - - ReleaseMutexValue(); - - KThread[] UnlockedThreads = new KThread[WaitingThreads.Count]; - - int Index = 0; - - while (WaitingThreads.TryDequeue(out KThread Thread)) - { - UnlockedThreads[Index++] = Thread; - } - - Process.Scheduler.Signal(UnlockedThreads); - } - } - - private void AcquireMutexValue() - { - if (!OwnsMutexValue) - { - while (!Process.Memory.AcquireAddress(MutexAddress)) - { - Thread.Yield(); - } - - OwnsMutexValue = true; - } - } - - private void ReleaseMutexValue() - { - if (OwnsMutexValue) - { - OwnsMutexValue = false; - - Process.Memory.ReleaseAddress(MutexAddress); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Process.cs b/Ryujinx.Core/OsHle/Process.cs index 0dd56dcb..f56103dc 100644 --- a/Ryujinx.Core/OsHle/Process.cs +++ b/Ryujinx.Core/OsHle/Process.cs @@ -5,8 +5,8 @@ using Ryujinx.Core.Loaders; using Ryujinx.Core.Loaders.Executables; using Ryujinx.Core.OsHle.Exceptions; using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.Kernel; using Ryujinx.Core.OsHle.Services.Nv; -using Ryujinx.Core.OsHle.Svc; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -55,17 +55,16 @@ namespace Ryujinx.Core.OsHle private bool Disposed; - public Process(Switch Ns, int ProcessId) + public Process(Switch Ns, KProcessScheduler Scheduler, int ProcessId) { this.Ns = Ns; + this.Scheduler = Scheduler; this.ProcessId = ProcessId; Memory = new AMemory(); HandleTable = new KProcessHandleTable(); - Scheduler = new KProcessScheduler(); - AppletState = new AppletStateMgr(); SvcHandler = new SvcHandler(Ns, this); @@ -127,7 +126,7 @@ namespace Ryujinx.Core.OsHle long StackTop = MemoryRegions.MainStackAddress + MemoryRegions.MainStackSize; - int Handle = MakeThread(Executables[0].ImageBase, StackTop, 0, 0, 0); + int Handle = MakeThread(Executables[0].ImageBase, StackTop, 0, 44, 0); if (Handle == -1) { @@ -188,28 +187,32 @@ namespace Ryujinx.Core.OsHle AThread Thread = new AThread(GetTranslator(), Memory, EntryPoint); - KThread ThreadHnd = new KThread(Thread, ProcessorId, Priority); + KThread KernelThread = new KThread(Thread, ProcessorId, Priority); + + int Handle = HandleTable.OpenHandle(KernelThread); - int Handle = HandleTable.OpenHandle(ThreadHnd); + KernelThread.Handle = Handle; int ThreadId = GetFreeTlsSlot(Thread); long Tpidr = MemoryRegions.TlsPagesAddress + ThreadId * TlsSize; + Thread.ThreadState.ProcessId = ProcessId; + Thread.ThreadState.ThreadId = ThreadId; + Thread.ThreadState.CntfrqEl0 = TickFreq; + Thread.ThreadState.Tpidr = Tpidr; + + Thread.ThreadState.X0 = (ulong)ArgsPtr; + Thread.ThreadState.X1 = (ulong)Handle; + Thread.ThreadState.X31 = (ulong)StackTop; + Thread.ThreadState.Break += BreakHandler; Thread.ThreadState.SvcCall += SvcHandler.SvcCall; Thread.ThreadState.Undefined += UndefinedHandler; - Thread.ThreadState.ProcessId = ProcessId; - Thread.ThreadState.ThreadId = ThreadId; - Thread.ThreadState.CntfrqEl0 = TickFreq; - Thread.ThreadState.Tpidr = Tpidr; - Thread.ThreadState.X0 = (ulong)ArgsPtr; - Thread.ThreadState.X1 = (ulong)Handle; - Thread.ThreadState.X31 = (ulong)StackTop; Thread.WorkFinished += ThreadFinished; - ThreadsByTpidr.TryAdd(Thread.ThreadState.Tpidr, ThreadHnd); + ThreadsByTpidr.TryAdd(Thread.ThreadState.Tpidr, KernelThread); return Handle; } @@ -293,6 +296,12 @@ namespace Ryujinx.Core.OsHle Logging.Info(LogClass.KernelScheduler, $"Thread {Thread.ThreadId} exiting..."); TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _); + + KThread KernelThread = GetThread(Thread.ThreadState.Tpidr); + + Scheduler.RemoveThread(KernelThread); + + KernelThread.WaitEvent.Set(); } if (TlsSlots.Count == 0) diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioOutManager.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioOutManager.cs index 986b5c1e..b1d20fbe 100644 --- a/Ryujinx.Core/OsHle/Services/Aud/IAudioOutManager.cs +++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioOutManager.cs @@ -76,7 +76,7 @@ namespace Ryujinx.Core.OsHle.Services.Aud ReleaseCallback Callback = () => { - ReleaseEvent.Handle.Set(); + ReleaseEvent.WaitEvent.Set(); }; int Track = AudioOut.OpenTrack(SampleRate, Channels, Callback, out AudioFormat Format); diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs index 9a20939e..d3795b53 100644 --- a/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs +++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs @@ -47,7 +47,7 @@ namespace Ryujinx.Core.OsHle.Services.Aud } //TODO: We shouldn't be signaling this here. - UpdateEvent.Handle.Set(); + UpdateEvent.WaitEvent.Set(); return 0; } diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs index 3ba4a45f..4dc01997 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs @@ -391,7 +391,7 @@ namespace Ryujinx.Core.OsHle.Services.Android { BufferQueue[Slot].State = BufferState.Free; - ReleaseEvent.Handle.Set(); + ReleaseEvent.WaitEvent.Set(); lock (WaitBufferFree) { diff --git a/Ryujinx.Core/OsHle/Svc/SvcHandler.cs b/Ryujinx.Core/OsHle/Svc/SvcHandler.cs deleted file mode 100644 index 9fea59a8..00000000 --- a/Ryujinx.Core/OsHle/Svc/SvcHandler.cs +++ /dev/null @@ -1,116 +0,0 @@ -using ChocolArm64.Events; -using ChocolArm64.Memory; -using ChocolArm64.State; -using Ryujinx.Core.OsHle.Handles; -using System; -using System.Collections.Generic; - -namespace Ryujinx.Core.OsHle.Svc -{ - partial class SvcHandler : IDisposable - { - private delegate void SvcFunc(AThreadState ThreadState); - - private Dictionary SvcFuncs; - - private Switch Ns; - private Process Process; - private AMemory Memory; - - private HashSet<(HSharedMem, long)> MappedSharedMems; - - private ulong CurrentHeapSize; - - private static Random Rng; - - public SvcHandler(Switch Ns, Process Process) - { - SvcFuncs = new Dictionary() - { - { 0x01, SvcSetHeapSize }, - { 0x03, SvcSetMemoryAttribute }, - { 0x04, SvcMapMemory }, - { 0x05, SvcUnmapMemory }, - { 0x06, SvcQueryMemory }, - { 0x07, SvcExitProcess }, - { 0x08, SvcCreateThread }, - { 0x09, SvcStartThread }, - { 0x0a, SvcExitThread }, - { 0x0b, SvcSleepThread }, - { 0x0c, SvcGetThreadPriority }, - { 0x0d, SvcSetThreadPriority }, - { 0x0f, SvcSetThreadCoreMask }, - { 0x10, SvcGetCurrentProcessorNumber }, - { 0x12, SvcClearEvent }, - { 0x13, SvcMapSharedMemory }, - { 0x14, SvcUnmapSharedMemory }, - { 0x15, SvcCreateTransferMemory }, - { 0x16, SvcCloseHandle }, - { 0x17, SvcResetSignal }, - { 0x18, SvcWaitSynchronization }, - { 0x1a, SvcArbitrateLock }, - { 0x1b, SvcArbitrateUnlock }, - { 0x1c, SvcWaitProcessWideKeyAtomic }, - { 0x1d, SvcSignalProcessWideKey }, - { 0x1e, SvcGetSystemTick }, - { 0x1f, SvcConnectToNamedPort }, - { 0x21, SvcSendSyncRequest }, - { 0x22, SvcSendSyncRequestWithUserBuffer }, - { 0x25, SvcGetThreadId }, - { 0x26, SvcBreak }, - { 0x27, SvcOutputDebugString }, - { 0x29, SvcGetInfo } - }; - - this.Ns = Ns; - this.Process = Process; - this.Memory = Process.Memory; - - MappedSharedMems = new HashSet<(HSharedMem, long)>(); - } - - static SvcHandler() - { - Rng = new Random(); - } - - public void SvcCall(object sender, AInstExceptionEventArgs e) - { - AThreadState ThreadState = (AThreadState)sender; - - if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func)) - { - Logging.Trace(LogClass.KernelSvc, $"(Thread {ThreadState.ThreadId}) {Func.Method.Name} called."); - - Func(ThreadState); - - Logging.Trace(LogClass.KernelSvc, $"(Thread {ThreadState.ThreadId}) {Func.Method.Name} ended."); - } - else - { - throw new NotImplementedException(e.Id.ToString("x4")); - } - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool Disposing) - { - if (Disposing) - { - lock (MappedSharedMems) - { - foreach ((HSharedMem SharedMem, long Position) in MappedSharedMems) - { - SharedMem.RemoveVirtualPosition(Memory, Position); - } - - MappedSharedMems.Clear(); - } - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs deleted file mode 100644 index 73485715..00000000 --- a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs +++ /dev/null @@ -1,264 +0,0 @@ -using ChocolArm64.Memory; -using ChocolArm64.State; -using Ryujinx.Core.OsHle.Handles; - -using static Ryujinx.Core.OsHle.ErrorCode; - -namespace Ryujinx.Core.OsHle.Svc -{ - partial class SvcHandler - { - private void SvcSetHeapSize(AThreadState ThreadState) - { - uint Size = (uint)ThreadState.X1; - - long Position = MemoryRegions.HeapRegionAddress; - - if (Size > CurrentHeapSize) - { - Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW); - } - else - { - Memory.Manager.Unmap(Position + Size, (long)CurrentHeapSize - Size); - } - - CurrentHeapSize = Size; - - ThreadState.X0 = 0; - ThreadState.X1 = (ulong)Position; - } - - private void SvcSetMemoryAttribute(AThreadState ThreadState) - { - long Position = (long)ThreadState.X0; - long Size = (long)ThreadState.X1; - int State0 = (int)ThreadState.X2; - int State1 = (int)ThreadState.X3; - - if ((State0 == 0 && State1 == 0) || - (State0 == 8 && State1 == 0)) - { - Memory.Manager.ClearAttrBit(Position, Size, 3); - } - else if (State0 == 8 && State1 == 8) - { - Memory.Manager.SetAttrBit(Position, Size, 3); - } - - ThreadState.X0 = 0; - } - - private void SvcMapMemory(AThreadState ThreadState) - { - long Dst = (long)ThreadState.X0; - long Src = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; - - if (!IsValidPosition(Src)) - { - Logging.Warn(LogClass.KernelSvc, $"Tried to map Memory at invalid src address {Src:x16}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); - - return; - } - - if (!IsValidMapPosition(Dst)) - { - Logging.Warn(LogClass.KernelSvc, $"Tried to map Memory at invalid dst address {Dst:x16}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); - - return; - } - - AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src); - - Memory.Manager.Map(Dst, Size, (int)MemoryType.MappedMemory, SrcInfo.Perm); - - Memory.Manager.Reprotect(Src, Size, AMemoryPerm.None); - - Memory.Manager.SetAttrBit(Src, Size, 0); - - ThreadState.X0 = 0; - } - - private void SvcUnmapMemory(AThreadState ThreadState) - { - long Dst = (long)ThreadState.X0; - long Src = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; - - if (!IsValidPosition(Src)) - { - Logging.Warn(LogClass.KernelSvc, $"Tried to unmap Memory at invalid src address {Src:x16}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); - - return; - } - - if (!IsValidMapPosition(Dst)) - { - Logging.Warn(LogClass.KernelSvc, $"Tried to unmap Memory at invalid dst address {Dst:x16}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); - - return; - } - - AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst); - - Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory); - - Memory.Manager.Reprotect(Src, Size, DstInfo.Perm); - - Memory.Manager.ClearAttrBit(Src, Size, 0); - - ThreadState.X0 = 0; - } - - private void SvcQueryMemory(AThreadState ThreadState) - { - long InfoPtr = (long)ThreadState.X0; - long Position = (long)ThreadState.X2; - - AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position); - - if (MapInfo == null) - { - long AddrSpaceEnd = MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize; - - long ReservedSize = (long)(ulong.MaxValue - (ulong)AddrSpaceEnd) + 1; - - MapInfo = new AMemoryMapInfo(AddrSpaceEnd, ReservedSize, (int)MemoryType.Reserved, 0, AMemoryPerm.None); - } - - Memory.WriteInt64(InfoPtr + 0x00, MapInfo.Position); - Memory.WriteInt64(InfoPtr + 0x08, MapInfo.Size); - Memory.WriteInt32(InfoPtr + 0x10, MapInfo.Type); - Memory.WriteInt32(InfoPtr + 0x14, MapInfo.Attr); - Memory.WriteInt32(InfoPtr + 0x18, (int)MapInfo.Perm); - Memory.WriteInt32(InfoPtr + 0x1c, 0); - Memory.WriteInt32(InfoPtr + 0x20, 0); - Memory.WriteInt32(InfoPtr + 0x24, 0); - //TODO: X1. - - ThreadState.X0 = 0; - ThreadState.X1 = 0; - } - - private void SvcMapSharedMemory(AThreadState ThreadState) - { - int Handle = (int)ThreadState.X0; - long Src = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; - int Perm = (int)ThreadState.X3; - - if (!IsValidPosition(Src)) - { - Logging.Warn(LogClass.KernelSvc, $"Tried to map SharedMemory at invalid address {Src:x16}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); - - return; - } - - HSharedMem SharedMem = Process.HandleTable.GetData(Handle); - - if (SharedMem != null) - { - Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, AMemoryPerm.Write); - - AMemoryHelper.FillWithZeros(Memory, Src, (int)Size); - - Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); - - lock (MappedSharedMems) - { - MappedSharedMems.Add((SharedMem, Src)); - } - - SharedMem.AddVirtualPosition(Memory, Src); - - ThreadState.X0 = 0; - } - - //TODO: Error codes. - } - - private void SvcUnmapSharedMemory(AThreadState ThreadState) - { - int Handle = (int)ThreadState.X0; - long Src = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; - - if (!IsValidPosition(Src)) - { - Logging.Warn(LogClass.KernelSvc, $"Tried to unmap SharedMemory at invalid address {Src:x16}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); - - return; - } - - HSharedMem SharedMem = Process.HandleTable.GetData(Handle); - - if (SharedMem != null) - { - Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory); - - SharedMem.RemoveVirtualPosition(Memory, Src); - - lock (MappedSharedMems) - { - MappedSharedMems.Remove((SharedMem, Src)); - } - - ThreadState.X0 = 0; - } - - //TODO: Error codes. - } - - private void SvcCreateTransferMemory(AThreadState ThreadState) - { - long Src = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; - int Perm = (int)ThreadState.X3; - - if (!IsValidPosition(Src)) - { - Logging.Warn(LogClass.KernelSvc, $"Tried to create TransferMemory at invalid address {Src:x16}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); - - return; - } - - AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Src); - - Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); - - HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size); - - ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem); - - ThreadState.X0 = 0; - ThreadState.X1 = Handle; - } - - private static bool IsValidPosition(long Position) - { - return Position >= MemoryRegions.AddrSpaceStart && - Position < MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize; - } - - private static bool IsValidMapPosition(long Position) - { - return Position >= MemoryRegions.MapRegionAddress && - Position < MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs deleted file mode 100644 index e615b429..00000000 --- a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs +++ /dev/null @@ -1,306 +0,0 @@ -using ChocolArm64.Memory; -using ChocolArm64.State; -using Ryujinx.Core.OsHle.Exceptions; -using Ryujinx.Core.OsHle.Handles; -using Ryujinx.Core.OsHle.Ipc; -using Ryujinx.Core.OsHle.Services; -using System; -using System.Threading; - -using static Ryujinx.Core.OsHle.ErrorCode; - -namespace Ryujinx.Core.OsHle.Svc -{ - partial class SvcHandler - { - private const int AllowedCpuIdBitmask = 0b1111; - - private const bool EnableProcessDebugging = false; - - private void SvcExitProcess(AThreadState ThreadState) - { - Ns.Os.ExitProcess(ThreadState.ProcessId); - } - - private void SvcClearEvent(AThreadState ThreadState) - { - int Handle = (int)ThreadState.X0; - - //TODO: Implement events. - - ThreadState.X0 = 0; - } - - private void SvcCloseHandle(AThreadState ThreadState) - { - int Handle = (int)ThreadState.X0; - - object Obj = Process.HandleTable.CloseHandle(Handle); - - if (Obj == null) - { - Logging.Warn(LogClass.KernelSvc, $"Tried to CloseHandle on invalid handle 0x{Handle:x8}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); - - return; - } - - if (Obj is KSession Session) - { - Session.Dispose(); - } - else if (Obj is HTransferMem TMem) - { - TMem.Memory.Manager.Reprotect( - TMem.Position, - TMem.Size, - TMem.Perm); - } - - ThreadState.X0 = 0; - } - - private void SvcResetSignal(AThreadState ThreadState) - { - int Handle = (int)ThreadState.X0; - - KEvent Event = Process.HandleTable.GetData(Handle); - - if (Event != null) - { - Event.Handle.Reset(); - - ThreadState.X0 = 0; - } - else - { - Logging.Warn(LogClass.KernelSvc, $"Tried to ResetSignal on invalid event handle 0x{Handle:x8}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); - } - } - - private void SvcWaitSynchronization(AThreadState ThreadState) - { - long HandlesPtr = (long)ThreadState.X1; - int HandlesCount = (int)ThreadState.X2; - long Timeout = (long)ThreadState.X3; - - KThread CurrThread = Process.GetThread(ThreadState.Tpidr); - - WaitHandle[] Handles = new WaitHandle[HandlesCount]; - - for (int Index = 0; Index < HandlesCount; Index++) - { - int Handle = Memory.ReadInt32(HandlesPtr + Index * 4); - - KSynchronizationObject SyncObj = Process.HandleTable.GetData(Handle); - - if (SyncObj == null) - { - Logging.Warn(LogClass.KernelSvc, $"Tried to WaitSynchronization on invalid handle 0x{Handle:x8}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); - - return; - } - - Handles[Index] = SyncObj.Handle; - } - - Process.Scheduler.Suspend(CurrThread.ProcessorId); - - int HandleIndex; - - ulong Result = 0; - - if (Timeout != -1) - { - HandleIndex = WaitHandle.WaitAny(Handles, (int)(Timeout / 1000000)); - - if (HandleIndex == WaitHandle.WaitTimeout) - { - Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout); - } - } - else - { - HandleIndex = WaitHandle.WaitAny(Handles); - } - - Process.Scheduler.Resume(CurrThread); - - ThreadState.X0 = Result; - - if (Result == 0) - { - ThreadState.X1 = (ulong)HandleIndex; - } - } - - private void SvcGetSystemTick(AThreadState ThreadState) - { - ThreadState.X0 = ThreadState.CntpctEl0; - } - - private void SvcConnectToNamedPort(AThreadState ThreadState) - { - long StackPtr = (long)ThreadState.X0; - long NamePtr = (long)ThreadState.X1; - - string Name = AMemoryHelper.ReadAsciiString(Memory, NamePtr, 8); - - //TODO: Validate that app has perms to access the service, and that the service - //actually exists, return error codes otherwise. - KSession Session = new KSession(ServiceFactory.MakeService(Name), Name); - - ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session); - - ThreadState.X0 = 0; - ThreadState.X1 = Handle; - } - - private void SvcSendSyncRequest(AThreadState ThreadState) - { - SendSyncRequest(ThreadState, ThreadState.Tpidr, 0x100, (int)ThreadState.X0); - } - - private void SvcSendSyncRequestWithUserBuffer(AThreadState ThreadState) - { - SendSyncRequest( - ThreadState, - (long)ThreadState.X0, - (long)ThreadState.X1, - (int)ThreadState.X2); - } - - private void SendSyncRequest(AThreadState ThreadState, long CmdPtr, long Size, int Handle) - { - KThread CurrThread = Process.GetThread(ThreadState.Tpidr); - - byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, Size); - - KSession Session = Process.HandleTable.GetData(Handle); - - if (Session != null) - { - Process.Scheduler.Suspend(CurrThread.ProcessorId); - - IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr); - - IpcHandler.IpcCall(Ns, Process, Memory, Session, Cmd, CmdPtr); - - Thread.Yield(); - - Process.Scheduler.Resume(CurrThread); - - ThreadState.X0 = 0; - } - else - { - Logging.Warn(LogClass.KernelSvc, $"Tried to SendSyncRequest on invalid session handle 0x{Handle:x8}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); - } - } - - private void SvcBreak(AThreadState ThreadState) - { - long Reason = (long)ThreadState.X0; - long Unknown = (long)ThreadState.X1; - long Info = (long)ThreadState.X2; - - throw new GuestBrokeExecutionException(); - } - - private void SvcOutputDebugString(AThreadState ThreadState) - { - long Position = (long)ThreadState.X0; - long Size = (long)ThreadState.X1; - - string Str = AMemoryHelper.ReadAsciiString(Memory, Position, Size); - - Logging.Info(LogClass.KernelSvc, Str); - - ThreadState.X0 = 0; - } - - private void SvcGetInfo(AThreadState ThreadState) - { - long StackPtr = (long)ThreadState.X0; - int InfoType = (int)ThreadState.X1; - long Handle = (long)ThreadState.X2; - int InfoId = (int)ThreadState.X3; - - //Fail for info not available on older Kernel versions. - if (InfoType == 18 || - InfoType == 19 || - InfoType == 20) - { - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo); - - return; - } - - switch (InfoType) - { - case 0: - ThreadState.X1 = AllowedCpuIdBitmask; - break; - - case 2: - ThreadState.X1 = MemoryRegions.MapRegionAddress; - break; - - case 3: - ThreadState.X1 = MemoryRegions.MapRegionSize; - break; - - case 4: - ThreadState.X1 = MemoryRegions.HeapRegionAddress; - break; - - case 5: - ThreadState.X1 = MemoryRegions.HeapRegionSize; - break; - - case 6: - ThreadState.X1 = MemoryRegions.TotalMemoryAvailable; - break; - - case 7: - ThreadState.X1 = MemoryRegions.TotalMemoryUsed + CurrentHeapSize; - break; - - case 8: - ThreadState.X1 = EnableProcessDebugging ? 1 : 0; - break; - - case 11: - ThreadState.X1 = (ulong)Rng.Next() + ((ulong)Rng.Next() << 32); - break; - - case 12: - ThreadState.X1 = MemoryRegions.AddrSpaceStart; - break; - - case 13: - ThreadState.X1 = MemoryRegions.AddrSpaceSize; - break; - - case 14: - ThreadState.X1 = MemoryRegions.MapRegionAddress; - break; - - case 15: - ThreadState.X1 = MemoryRegions.MapRegionSize; - break; - - default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}"); - } - - ThreadState.X0 = 0; - } - } -} diff --git a/Ryujinx.Core/OsHle/Svc/SvcThread.cs b/Ryujinx.Core/OsHle/Svc/SvcThread.cs deleted file mode 100644 index 4dc9e15c..00000000 --- a/Ryujinx.Core/OsHle/Svc/SvcThread.cs +++ /dev/null @@ -1,146 +0,0 @@ -using ChocolArm64.State; -using Ryujinx.Core.OsHle.Handles; - -using static Ryujinx.Core.OsHle.ErrorCode; - -namespace Ryujinx.Core.OsHle.Svc -{ - partial class SvcHandler - { - private void SvcCreateThread(AThreadState ThreadState) - { - long EntryPoint = (long)ThreadState.X1; - long ArgsPtr = (long)ThreadState.X2; - long StackTop = (long)ThreadState.X3; - int Priority = (int)ThreadState.X4; - int ProcessorId = (int)ThreadState.X5; - - if (Ns.Os.TryGetProcess(ThreadState.ProcessId, out Process Process)) - { - if (ProcessorId == -2) - { - //TODO: Get this value from the NPDM file. - ProcessorId = 0; - } - - int Handle = Process.MakeThread( - EntryPoint, - StackTop, - ArgsPtr, - Priority, - ProcessorId); - - ThreadState.X0 = 0; - ThreadState.X1 = (ulong)Handle; - } - - //TODO: Error codes. - } - - private void SvcStartThread(AThreadState ThreadState) - { - int Handle = (int)ThreadState.X0; - - KThread Thread = Process.HandleTable.GetData(Handle); - - if (Thread != null) - { - Process.Scheduler.StartThread(Thread); - - ThreadState.X0 = 0; - } - - //TODO: Error codes. - } - - private void SvcExitThread(AThreadState ThreadState) - { - KThread CurrThread = Process.GetThread(ThreadState.Tpidr); - - CurrThread.Thread.StopExecution(); - - CurrThread.Handle.Set(); - } - - private void SvcSleepThread(AThreadState ThreadState) - { - ulong NanoSecs = ThreadState.X0; - - KThread CurrThread = Process.GetThread(ThreadState.Tpidr); - - if (NanoSecs == 0) - { - Process.Scheduler.Yield(CurrThread); - } - else - { - Process.Scheduler.WaitForSignal(CurrThread, (int)(NanoSecs / 1000000)); - } - } - - private void SvcGetThreadPriority(AThreadState ThreadState) - { - int Handle = (int)ThreadState.X1; - - KThread Thread = Process.HandleTable.GetData(Handle); - - if (Thread != null) - { - ThreadState.X0 = 0; - ThreadState.X1 = (ulong)Thread.Priority; - } - - //TODO: Error codes. - } - - private void SvcSetThreadPriority(AThreadState ThreadState) - { - int Prio = (int)ThreadState.X0; - int Handle = (int)ThreadState.X1; - - KThread Thread = Process.HandleTable.GetData(Handle); - - if (Thread != null) - { - Thread.Priority = Prio; - - ThreadState.X0 = 0; - } - - //TODO: Error codes. - } - - private void SvcSetThreadCoreMask(AThreadState ThreadState) - { - ThreadState.X0 = 0; - - //TODO: Error codes. - } - - private void SvcGetCurrentProcessorNumber(AThreadState ThreadState) - { - KThread CurrThread = Process.GetThread(ThreadState.Tpidr); - - ThreadState.X0 = (ulong)CurrThread.ProcessorId; - } - - private void SvcGetThreadId(AThreadState ThreadState) - { - int Handle = (int)ThreadState.X1; - - KThread Thread = Process.HandleTable.GetData(Handle); - - if (Thread != null) - { - ThreadState.X0 = 0; - ThreadState.X1 = (ulong)Thread.ThreadId; - } - else - { - Logging.Warn(LogClass.KernelSvc, $"Tried to GetThreadId on invalid thread handle 0x{Handle:x8}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs b/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs deleted file mode 100644 index 318688b8..00000000 --- a/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs +++ /dev/null @@ -1,85 +0,0 @@ -using ChocolArm64.State; -using Ryujinx.Core.OsHle.Handles; - -using static Ryujinx.Core.OsHle.ErrorCode; - -namespace Ryujinx.Core.OsHle.Svc -{ - partial class SvcHandler - { - private void SvcArbitrateLock(AThreadState ThreadState) - { - int OwnerThreadHandle = (int)ThreadState.X0; - long MutexAddress = (long)ThreadState.X1; - int RequestingThreadHandle = (int)ThreadState.X2; - - KThread RequestingThread = Process.HandleTable.GetData(RequestingThreadHandle); - - Mutex M = new Mutex(Process, MutexAddress, OwnerThreadHandle); - - M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M); - - M.WaitForLock(RequestingThread, RequestingThreadHandle); - - ThreadState.X0 = 0; - } - - private void SvcArbitrateUnlock(AThreadState ThreadState) - { - long MutexAddress = (long)ThreadState.X0; - - if (Ns.Os.Mutexes.TryGetValue(MutexAddress, out Mutex M)) - { - M.Unlock(); - } - - ThreadState.X0 = 0; - } - - private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState) - { - long MutexAddress = (long)ThreadState.X0; - long CondVarAddress = (long)ThreadState.X1; - int ThreadHandle = (int)ThreadState.X2; - long Timeout = (long)ThreadState.X3; - - KThread Thread = Process.HandleTable.GetData(ThreadHandle); - - Mutex M = new Mutex(Process, MutexAddress, ThreadHandle); - - M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M); - - M.GiveUpLock(ThreadHandle); - - CondVar Cv = new CondVar(Process, CondVarAddress, Timeout); - - Cv = Ns.Os.CondVars.GetOrAdd(CondVarAddress, Cv); - - if (!Cv.WaitForSignal(Thread)) - { - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.Timeout); - - return; - } - - M.WaitForLock(Thread, ThreadHandle); - - ThreadState.X0 = 0; - } - - private void SvcSignalProcessWideKey(AThreadState ThreadState) - { - long CondVarAddress = (long)ThreadState.X0; - int Count = (int)ThreadState.X1; - - KThread CurrThread = Process.GetThread(ThreadState.Tpidr); - - if (Ns.Os.CondVars.TryGetValue(CondVarAddress, out CondVar Cv)) - { - Cv.SetSignal(CurrThread, Count); - } - - ThreadState.X0 = 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index 5e3e1e65..6b6ae6a0 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -1,6 +1,5 @@ using OpenTK; using OpenTK.Graphics; -using OpenTK.Graphics.OpenGL; using OpenTK.Input; using Ryujinx.Core; using Ryujinx.Core.Input; -- cgit v1.2.3