aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Core/OsHle/Process.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Core/OsHle/Process.cs')
-rw-r--r--Ryujinx.Core/OsHle/Process.cs257
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