diff options
Diffstat (limited to 'Ryujinx.Core/OsHle/Process.cs')
| -rw-r--r-- | Ryujinx.Core/OsHle/Process.cs | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/Ryujinx.Core/OsHle/Process.cs b/Ryujinx.Core/OsHle/Process.cs new file mode 100644 index 00000000..84267885 --- /dev/null +++ b/Ryujinx.Core/OsHle/Process.cs @@ -0,0 +1,257 @@ +using ChocolArm64; +using ChocolArm64.Memory; +using ChocolArm64.State; +using Ryujinx.Core.Loaders; +using Ryujinx.Core.Loaders.Executables; +using Ryujinx.Core.OsHle.Exceptions; +using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.Svc; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.Core.OsHle +{ + public class Process : IDisposable + { + private const int MaxStackSize = 8 * 1024 * 1024; + + private const int TlsSize = 0x200; + private const int TotalTlsSlots = 32; + private const int TlsTotalSize = TotalTlsSlots * TlsSize; + private const long TlsPageAddr = (AMemoryMgr.AddrSize - TlsTotalSize) & ~AMemoryMgr.PageMask; + + private Switch Ns; + + public int ProcessId { get; private set; } + + public AMemory Memory { get; private set; } + + public KProcessScheduler Scheduler { get; private set; } + + private SvcHandler SvcHandler; + + private ConcurrentDictionary<int, AThread> TlsSlots; + + private ConcurrentDictionary<long, HThread> ThreadsByTpidr; + + private List<Executable> Executables; + + private HThread MainThread; + + private long ImageBase; + + public Process(Switch Ns, AMemoryAlloc Allocator, int ProcessId) + { + this.Ns = Ns; + this.ProcessId = ProcessId; + + Memory = new AMemory(Ns.Ram, Allocator); + + Scheduler = new KProcessScheduler(); + + SvcHandler = new SvcHandler(Ns, this); + + TlsSlots = new ConcurrentDictionary<int, AThread>(); + + ThreadsByTpidr = new ConcurrentDictionary<long, HThread>(); + + Executables = new List<Executable>(); + + ImageBase = 0x8000000; + + Memory.Manager.MapPhys( + TlsPageAddr, + TlsTotalSize, + (int)MemoryType.ThreadLocal, + AMemoryPerm.RW); + } + + public void LoadProgram(IExecutable Program) + { + Logging.Info($"Image base at 0x{ImageBase:x16}."); + + Executable Executable = new Executable(Program, Memory, ImageBase); + + Executables.Add(Executable); + + ImageBase = AMemoryHelper.PageRoundUp(Executable.ImageEnd); + } + + public void SetEmptyArgs() + { + ImageBase += AMemoryMgr.PageSize; + } + + public void InitializeHeap() + { + Memory.Manager.SetHeapAddr((ImageBase + 0x3fffffff) & ~0x3fffffff); + } + + public bool Run() + { + if (Executables.Count == 0) + { + return false; + } + + long StackBot = TlsPageAddr - MaxStackSize; + + Memory.Manager.MapPhys(StackBot, MaxStackSize, (int)MemoryType.Normal, AMemoryPerm.RW); + + int Handle = MakeThread(Executables[0].ImageBase, TlsPageAddr, 0, 0, 0); + + if (Handle == -1) + { + return false; + } + + MainThread = Ns.Os.Handles.GetData<HThread>(Handle); + + Scheduler.StartThread(MainThread); + + return true; + } + + public void StopAllThreads() + { + if (MainThread != null) + { + while (MainThread.Thread.IsAlive) + { + MainThread.Thread.StopExecution(); + } + } + + foreach (AThread Thread in TlsSlots.Values) + { + while (Thread.IsAlive) + { + Thread.StopExecution(); + } + } + } + + public int MakeThread( + long EntryPoint, + long StackTop, + long ArgsPtr, + int Priority, + int ProcessorId) + { + ThreadPriority ThreadPrio; + + if (Priority < 12) + { + ThreadPrio = ThreadPriority.Highest; + } + else if (Priority < 24) + { + ThreadPrio = ThreadPriority.AboveNormal; + } + else if (Priority < 36) + { + ThreadPrio = ThreadPriority.Normal; + } + else if (Priority < 48) + { + ThreadPrio = ThreadPriority.BelowNormal; + } + else + { + ThreadPrio = ThreadPriority.Lowest; + } + + AThread Thread = new AThread(Memory, ThreadPrio, EntryPoint); + + HThread ThreadHnd = new HThread(Thread, ProcessorId, Priority); + + int Handle = Ns.Os.Handles.GenerateId(ThreadHnd); + + int TlsSlot = GetFreeTlsSlot(Thread); + + if (TlsSlot == -1 || Handle == -1) + { + return -1; + } + + Thread.ThreadState.Break += BreakHandler; + Thread.ThreadState.SvcCall += SvcHandler.SvcCall; + Thread.ThreadState.Undefined += UndefinedHandler; + Thread.ThreadState.ProcessId = ProcessId; + Thread.ThreadState.ThreadId = Ns.Os.IdGen.GenerateId(); + Thread.ThreadState.Tpidr = TlsPageAddr + TlsSlot * TlsSize; + Thread.ThreadState.X0 = (ulong)ArgsPtr; + Thread.ThreadState.X1 = (ulong)Handle; + Thread.ThreadState.X31 = (ulong)StackTop; + + Thread.WorkFinished += ThreadFinished; + + ThreadsByTpidr.TryAdd(Thread.ThreadState.Tpidr, ThreadHnd); + + return Handle; + } + + private void BreakHandler(object sender, AInstExceptEventArgs e) + { + throw new GuestBrokeExecutionException(); + } + + private void UndefinedHandler(object sender, AInstUndEventArgs e) + { + throw new UndefinedInstructionException(e.Position, e.RawOpCode); + } + + private int GetFreeTlsSlot(AThread Thread) + { + for (int Index = 1; Index < TotalTlsSlots; Index++) + { + if (TlsSlots.TryAdd(Index, Thread)) + { + return Index; + } + } + + return -1; + } + + private void ThreadFinished(object sender, EventArgs e) + { + if (sender is AThread Thread) + { + TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _); + + Ns.Os.IdGen.DeleteId(Thread.ThreadId); + } + } + + private int GetTlsSlot(long Position) + { + return (int)((Position - TlsPageAddr) / TlsSize); + } + + public HThread GetThread(long Tpidr) + { + if (!ThreadsByTpidr.TryGetValue(Tpidr, out HThread Thread)) + { + Logging.Error($"Thread with TPIDR 0x{Tpidr:x16} not found!"); + } + + return Thread; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + Scheduler.Dispose(); + } + } + } +}
\ No newline at end of file |
