ryujinx/Ryujinx.Core/OsHle/Process.cs

264 lines
7.2 KiB
C#
Raw Normal View History

2018-02-04 18:08:20 -05:00
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;
2018-02-04 18:08:20 -05:00
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
2018-02-04 18:08:20 -05:00
namespace Ryujinx.Core.OsHle
2018-02-04 18:08:20 -05:00
{
public class Process : IDisposable
2018-02-04 18:08:20 -05:00
{
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; }
2018-02-04 18:08:20 -05:00
2018-02-14 00:43:21 -05:00
private SvcHandler SvcHandler;
2018-02-04 18:08:20 -05:00
private ConcurrentDictionary<int, AThread> TlsSlots;
private ConcurrentDictionary<long, HThread> ThreadsByTpidr;
2018-02-04 18:08:20 -05:00
private List<Executable> Executables;
private HThread MainThread;
2018-02-04 18:08:20 -05:00
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>();
2018-02-04 18:08:20 -05:00
Executables = new List<Executable>();
ImageBase = 0x8000000;
Memory.Manager.MapPhys(
TlsPageAddr,
TlsTotalSize,
(int)MemoryType.ThreadLocal,
AMemoryPerm.RW);
}
public void LoadProgram(IExecutable Program)
2018-02-04 18:08:20 -05:00
{
Logging.Info($"Image base at 0x{ImageBase:x16}.");
2018-02-04 18:08:20 -05:00
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);
2018-02-04 18:08:20 -05:00
if (Handle == -1)
{
return false;
}
MainThread = Ns.Os.Handles.GetData<HThread>(Handle);
2018-02-04 18:08:20 -05:00
Scheduler.StartThread(MainThread);
2018-02-04 18:08:20 -05:00
return true;
}
public void StopAllThreads()
{
if (MainThread != null)
{
while (MainThread.Thread.IsAlive)
2018-02-04 18:08:20 -05:00
{
MainThread.Thread.StopExecution();
2018-02-04 18:08:20 -05:00
}
}
foreach (AThread Thread in TlsSlots.Values)
{
while (Thread.IsAlive)
2018-02-04 18:08:20 -05:00
{
Thread.StopExecution();
}
}
}
public int MakeThread(
long EntryPoint,
long StackTop,
long ArgsPtr,
int Priority,
int ProcessorId)
{
ThreadPriority ThreadPrio;
2018-02-04 18:08:20 -05:00
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);
2018-02-04 18:08:20 -05:00
int Handle = Ns.Os.Handles.GenerateId(ThreadHnd);
int TlsSlot = GetFreeTlsSlot(Thread);
2018-02-04 18:08:20 -05:00
if (TlsSlot == -1 || Handle == -1)
{
return -1;
}
2018-02-18 14:28:07 -05:00
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;
2018-02-04 18:08:20 -05:00
if (Executables[0].Extension == Extensions.NRO)
{
Homebrew Homebrew_ABI = new Homebrew(Memory, Executables[0].ImageEnd, (long)Handle);
Thread.ThreadState.X0 = (ulong)Executables[0].ImageEnd;
Thread.ThreadState.X1 = 0xFFFFFFFFFFFFFFFF;
}
2018-02-04 18:08:20 -05:00
Thread.WorkFinished += ThreadFinished;
2018-02-18 14:28:07 -05:00
ThreadsByTpidr.TryAdd(Thread.ThreadState.Tpidr, ThreadHnd);
2018-02-04 18:08:20 -05:00
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);
}
2018-02-04 18:08:20 -05:00
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)
{
2018-02-18 14:28:07 -05:00
TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _);
2018-02-04 18:08:20 -05:00
Ns.Os.IdGen.DeleteId(Thread.ThreadId);
}
}
private int GetTlsSlot(long Position)
{
return (int)((Position - TlsPageAddr) / TlsSize);
}
2018-02-19 14:37:13 -05:00
public HThread GetThread(long Tpidr)
{
2018-02-19 14:37:13 -05:00
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();
}
}
2018-02-04 18:08:20 -05:00
}
}