ryujinx/Ryujinx.HLE/HOS/Process.cs

431 lines
12 KiB
C#
Raw Normal View History

2018-02-05 00:08:20 +01:00
using ChocolArm64;
using ChocolArm64.Events;
2018-02-05 00:08:20 +01:00
using ChocolArm64.Memory;
using ChocolArm64.State;
using LibHac;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Diagnostics.Demangler;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Services.Nv;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Loaders;
using Ryujinx.HLE.Loaders.Executables;
2018-08-02 22:14:49 +02:00
using Ryujinx.HLE.Loaders.Npdm;
using Ryujinx.HLE.Logging;
using Ryujinx.HLE.Utilities;
2018-02-05 00:08:20 +01:00
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Text;
2018-02-05 00:08:20 +01:00
namespace Ryujinx.HLE.HOS
2018-02-05 00:08:20 +01:00
{
class Process : IDisposable
2018-02-05 00:08:20 +01:00
{
private const int TickFreq = 19_200_000;
public Switch Device { get; private set; }
2018-02-05 00:08:20 +01:00
public bool NeedsHbAbi { get; private set; }
public long HbAbiDataPosition { get; private set; }
2018-02-05 00:08:20 +01:00
public int ProcessId { get; private set; }
private ATranslator Translator;
2018-02-05 00:08:20 +01:00
public AMemory Memory { get; private set; }
public KMemoryManager MemoryManager { get; private set; }
private List<KTlsPageManager> TlsPages;
public Npdm MetaData { get; private set; }
public Nacp ControlData { get; set; }
public KProcessHandleTable HandleTable { get; private set; }
public AppletStateMgr AppletState { get; private set; }
2018-02-14 06:43:21 +01:00
private SvcHandler SvcHandler;
2018-02-05 00:08:20 +01:00
private ConcurrentDictionary<long, KThread> Threads;
2018-02-05 00:08:20 +01:00
private List<Executable> Executables;
private Dictionary<long, string> SymbolTable;
2018-02-05 00:08:20 +01:00
private long ImageBase;
private bool Disposed;
public Process(Switch Device, int ProcessId, Npdm MetaData)
2018-02-05 00:08:20 +01:00
{
this.Device = Device;
this.MetaData = MetaData;
2018-02-05 00:08:20 +01:00
this.ProcessId = ProcessId;
Memory = new AMemory(Device.Memory.RamPointer);
MemoryManager = new KMemoryManager(this);
TlsPages = new List<KTlsPageManager>();
HandleTable = new KProcessHandleTable();
AppletState = new AppletStateMgr(Device.System);
SvcHandler = new SvcHandler(Device, this);
Threads = new ConcurrentDictionary<long, KThread>();
2018-02-05 00:08:20 +01:00
Executables = new List<Executable>();
ImageBase = MemoryManager.CodeRegionStart;
2018-02-05 00:08:20 +01:00
}
public void LoadProgram(IExecutable Program)
2018-02-05 00:08:20 +01:00
{
if (Disposed)
{
throw new ObjectDisposedException(nameof(Process));
}
Device.Log.PrintInfo(LogClass.Loader, $"Image base at 0x{ImageBase:x16}.");
Executable Executable = new Executable(Program, MemoryManager, Memory, ImageBase);
2018-02-05 00:08:20 +01:00
Executables.Add(Executable);
ImageBase = IntUtils.AlignUp(Executable.ImageEnd, KMemoryManager.PageSize);
2018-02-05 00:08:20 +01:00
}
public void SetEmptyArgs()
{
//TODO: This should be part of Run.
ImageBase += KMemoryManager.PageSize;
2018-02-05 00:08:20 +01:00
}
public bool Run(bool NeedsHbAbi = false)
2018-02-05 00:08:20 +01:00
{
if (Disposed)
{
throw new ObjectDisposedException(nameof(Process));
}
this.NeedsHbAbi = NeedsHbAbi;
2018-02-05 00:08:20 +01:00
if (Executables.Count == 0)
{
return false;
}
MakeSymbolTable();
long MainStackTop = MemoryManager.CodeRegionEnd - KMemoryManager.PageSize;
long MainStackSize = 1 * 1024 * 1024;
2018-04-05 00:29:34 +02:00
long MainStackBottom = MainStackTop - MainStackSize;
2018-02-05 00:08:20 +01:00
MemoryManager.HleMapCustom(
MainStackBottom,
MainStackSize,
MemoryState.MappedMemory,
MemoryPermission.ReadAndWrite);
int Handle = MakeThread(Executables[0].ImageBase, MainStackTop, 0, 44, 0);
2018-02-05 00:08:20 +01:00
if (Handle == -1)
{
return false;
}
KThread MainThread = HandleTable.GetData<KThread>(Handle);
2018-02-05 00:08:20 +01:00
if (NeedsHbAbi)
{
HbAbiDataPosition = IntUtils.AlignUp(Executables[0].ImageEnd, KMemoryManager.PageSize);
const long HbAbiDataSize = KMemoryManager.PageSize;
MemoryManager.HleMapCustom(
HbAbiDataPosition,
HbAbiDataSize,
MemoryState.MappedMemory,
MemoryPermission.ReadAndWrite);
string SwitchPath = Device.FileSystem.SystemPathToSwitchPath(Executables[0].FilePath);
Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle, SwitchPath);
MainThread.Context.ThreadState.X0 = (ulong)HbAbiDataPosition;
MainThread.Context.ThreadState.X1 = ulong.MaxValue;
}
MainThread.TimeUp();
2018-02-05 00:08:20 +01:00
return true;
}
private int ThreadIdCtr = 1;
2018-02-05 00:08:20 +01:00
public int MakeThread(
long EntryPoint,
long StackTop,
long ArgsPtr,
int Priority,
int ProcessorId)
{
if (Disposed)
{
throw new ObjectDisposedException(nameof(Process));
}
AThread CpuThread = new AThread(GetTranslator(), Memory, EntryPoint);
long Tpidr = GetFreeTls();
2018-02-05 00:08:20 +01:00
int ThreadId = ThreadIdCtr++; //(int)((Tpidr - MemoryManager.TlsIoRegionStart) / 0x200) + 1;
2018-06-26 06:09:32 +02:00
KThread Thread = new KThread(CpuThread, this, Device.System, ProcessorId, Priority, ThreadId);
2018-02-05 00:08:20 +01:00
Thread.LastPc = EntryPoint;
2018-02-05 00:08:20 +01:00
int Handle = HandleTable.OpenHandle(Thread);
CpuThread.ThreadState.CntfrqEl0 = TickFreq;
CpuThread.ThreadState.Tpidr = Tpidr;
CpuThread.ThreadState.X0 = (ulong)ArgsPtr;
CpuThread.ThreadState.X1 = (ulong)Handle;
CpuThread.ThreadState.X31 = (ulong)StackTop;
CpuThread.ThreadState.Interrupt += InterruptHandler;
CpuThread.ThreadState.Break += BreakHandler;
CpuThread.ThreadState.SvcCall += SvcHandler.SvcCall;
CpuThread.ThreadState.Undefined += UndefinedHandler;
2018-02-05 00:08:20 +01:00
CpuThread.WorkFinished += ThreadFinished;
2018-02-05 00:08:20 +01:00
Threads.TryAdd(CpuThread.ThreadState.Tpidr, Thread);
2018-02-05 00:08:20 +01:00
return Handle;
}
private long GetFreeTls()
{
long Position;
lock (TlsPages)
{
for (int Index = 0; Index < TlsPages.Count; Index++)
{
if (TlsPages[Index].TryGetFreeTlsAddr(out Position))
{
return Position;
}
}
long PagePosition = MemoryManager.HleMapTlsPage();
KTlsPageManager TlsPage = new KTlsPageManager(PagePosition);
TlsPages.Add(TlsPage);
TlsPage.TryGetFreeTlsAddr(out Position);
}
return Position;
}
private void InterruptHandler(object sender, EventArgs e)
{
Device.System.Scheduler.ContextSwitch();
}
private void BreakHandler(object sender, AInstExceptionEventArgs e)
{
throw new GuestBrokeExecutionException();
}
private void UndefinedHandler(object sender, AInstUndefinedEventArgs e)
{
throw new UndefinedInstructionException(e.Position, e.RawOpCode);
}
private void MakeSymbolTable()
{
SymbolTable = new Dictionary<long, string>();
foreach (Executable Exe in Executables)
{
foreach (KeyValuePair<long, string> KV in Exe.SymbolTable)
{
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
}
}
}
private ATranslator GetTranslator()
{
if (Translator == null)
{
Translator = new ATranslator(SymbolTable);
Translator.CpuTrace += CpuTraceHandler;
}
return Translator;
}
public void EnableCpuTracing()
{
Translator.EnableCpuTrace = true;
}
public void DisableCpuTracing()
{
Translator.EnableCpuTrace = false;
}
private void CpuTraceHandler(object sender, ACpuTraceEventArgs e)
{
string NsoName = string.Empty;
for (int Index = Executables.Count - 1; Index >= 0; Index--)
{
if (e.Position >= Executables[Index].ImageBase)
{
NsoName = $"{(e.Position - Executables[Index].ImageBase):x16}";
2018-04-05 00:29:34 +02:00
break;
}
}
Device.Log.PrintDebug(LogClass.Cpu, $"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
}
public void PrintStackTrace(AThreadState ThreadState)
{
long[] Positions = ThreadState.GetCallStack();
StringBuilder Trace = new StringBuilder();
Trace.AppendLine("Guest stack trace:");
foreach (long Position in Positions)
{
if (!SymbolTable.TryGetValue(Position, out string SubName))
{
SubName = $"Sub{Position:x16}";
}
else if (SubName.StartsWith("_Z"))
{
SubName = Demangler.Parse(SubName);
}
Trace.AppendLine(" " + SubName + " (" + GetNsoNameAndAddress(Position) + ")");
}
Device.Log.PrintInfo(LogClass.Cpu, Trace.ToString());
}
private string GetNsoNameAndAddress(long Position)
{
string Name = string.Empty;
for (int Index = Executables.Count - 1; Index >= 0; Index--)
{
if (Position >= Executables[Index].ImageBase)
{
long Offset = Position - Executables[Index].ImageBase;
Name = $"{Executables[Index].Name}:{Offset:x8}";
break;
}
}
return Name;
}
2018-02-05 00:08:20 +01:00
private void ThreadFinished(object sender, EventArgs e)
{
if (sender is AThread Thread)
{
if (Threads.TryRemove(Thread.ThreadState.Tpidr, out KThread KernelThread))
{
Device.System.Scheduler.RemoveThread(KernelThread);
}
}
2018-02-05 00:08:20 +01:00
if (Threads.Count == 0)
{
Device.System.ExitProcess(ProcessId);
2018-02-05 00:08:20 +01:00
}
}
public KThread GetThread(long Tpidr)
{
if (!Threads.TryGetValue(Tpidr, out KThread Thread))
2018-02-19 20:37:13 +01:00
{
2018-04-24 20:57:39 +02:00
throw new InvalidOperationException();
2018-02-19 20:37:13 +01:00
}
return Thread;
}
private void Unload()
{
if (Disposed || Threads.Count > 0)
{
return;
}
Disposed = true;
foreach (object Obj in HandleTable.Clear())
{
if (Obj is KSession Session)
{
Session.Dispose();
}
}
INvDrvServices.UnloadProcess(this);
if (NeedsHbAbi && Executables.Count > 0 && Executables[0].FilePath.EndsWith(Homebrew.TemporaryNroSuffix))
{
File.Delete(Executables[0].FilePath);
}
Device.Log.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting...");
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{
if (Threads.Count > 0)
{
foreach (KThread Thread in Threads.Values)
{
Device.System.Scheduler.StopThread(Thread);
}
}
else
{
Unload();
}
}
}
2018-02-05 00:08:20 +01:00
}
}