using ChocolArm64; using ChocolArm64.Memory; using Ryujinx.Loaders; using Ryujinx.Loaders.Executables; using Ryujinx.OsHle.Handles; using Ryujinx.OsHle.Svc; using System; using System.Collections.Concurrent; using System.Collections.Generic; namespace Ryujinx.OsHle { class Process { 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; } private SvcHandler SvcHandler; private AThread MainThread; private ConcurrentDictionary TlsSlots; private List Executables; private long ImageBase; public Process(Switch Ns, AMemoryAlloc Allocator, int ProcessId) { this.Ns = Ns; this.ProcessId = ProcessId; Memory = new AMemory(Ns.Ram, Allocator); SvcHandler = new SvcHandler(Ns, Memory); TlsSlots = new ConcurrentDictionary(); Executables = new List(); ImageBase = 0x8000000; Memory.Manager.MapPhys( TlsPageAddr, TlsTotalSize, (int)MemoryType.ThreadLocal, AMemoryPerm.RW); } public void LoadProgram(IElf Program) { 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, 48, 0); if (Handle == -1) { return false; } MainThread = Ns.Os.Handles.GetData(Handle).Thread; MainThread.Execute(); return true; } public void StopAllThreads() { if (MainThread != null) { while (MainThread.IsAlive) { MainThread.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) { AThread Thread = new AThread(Memory, EntryPoint, Priority); int TlsSlot = GetFreeTlsSlot(Thread); int Handle = Ns.Os.Handles.GenerateId(new HThread(Thread)); if (TlsSlot == -1 || Handle == -1) { return -1; } Thread.Registers.SvcCall += SvcHandler.SvcCall; Thread.Registers.ProcessId = ProcessId; Thread.Registers.ThreadId = Ns.Os.IdGen.GenerateId(); Thread.Registers.Tpidr = TlsPageAddr + TlsSlot * TlsSize; Thread.Registers.X0 = (ulong)ArgsPtr; Thread.Registers.X1 = (ulong)Handle; Thread.Registers.X31 = (ulong)StackTop; Thread.WorkFinished += ThreadFinished; return Handle; } 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.Registers.Tpidr), out _); Ns.Os.IdGen.DeleteId(Thread.ThreadId); } } private int GetTlsSlot(long Position) { return (int)((Position - TlsPageAddr) / TlsSize); } } }