Archived
1
0
Fork 0
forked from Mirror/Ryujinx

Allow more than one process, free resources on process dispose, implement SvcExitThread

This commit is contained in:
gdkchan 2018-03-12 01:04:52 -03:00
parent 3aaa4717b6
commit 7a27990faa
46 changed files with 926 additions and 598 deletions

View file

@ -14,43 +14,30 @@ namespace ChocolArm64
private ATranslator Translator; private ATranslator Translator;
private ThreadPriority Priority;
private Thread Work; private Thread Work;
public event EventHandler WorkFinished; public event EventHandler WorkFinished;
public int ThreadId => ThreadState.ThreadId; public int ThreadId => ThreadState.ThreadId;
public bool IsAlive => Work.IsAlive; private int IsExecuting;
private bool IsExecuting; public AThread(ATranslator Translator, AMemory Memory, long EntryPoint)
private object ExecuteLock;
public AThread(ATranslator Translator, AMemory Memory, ThreadPriority Priority, long EntryPoint)
{ {
this.Translator = Translator; this.Translator = Translator;
this.Memory = Memory; this.Memory = Memory;
this.Priority = Priority;
this.EntryPoint = EntryPoint; this.EntryPoint = EntryPoint;
ThreadState = new AThreadState(); ThreadState = new AThreadState();
ExecuteLock = new object();
}
public void StopExecution() => Translator.StopExecution(); ThreadState.Running = true;
}
public bool Execute() public bool Execute()
{ {
lock (ExecuteLock) if (Interlocked.Exchange(ref IsExecuting, 1) == 1)
{ {
if (IsExecuting) return false;
{
return false;
}
IsExecuting = true;
} }
Work = new Thread(delegate() Work = new Thread(delegate()
@ -62,11 +49,11 @@ namespace ChocolArm64
WorkFinished?.Invoke(this, EventArgs.Empty); WorkFinished?.Invoke(this, EventArgs.Empty);
}); });
Work.Priority = Priority;
Work.Start(); Work.Start();
return true; return true;
} }
public void StopExecution() => ThreadState.Running = false;
} }
} }

View file

@ -22,8 +22,6 @@ namespace ChocolArm64
public bool EnableCpuTrace { get; set; } public bool EnableCpuTrace { get; set; }
private bool KeepRunning;
public ATranslator(IReadOnlyDictionary<long, string> SymbolTable = null) public ATranslator(IReadOnlyDictionary<long, string> SymbolTable = null)
{ {
SubBlocks = new HashSet<long>(); SubBlocks = new HashSet<long>();
@ -38,12 +36,8 @@ namespace ChocolArm64
{ {
this.SymbolTable = new ConcurrentDictionary<long, string>(); this.SymbolTable = new ConcurrentDictionary<long, string>();
} }
KeepRunning = true;
} }
internal void StopExecution() => KeepRunning = false;
internal void ExecuteSubroutine(AThread Thread, long Position) internal void ExecuteSubroutine(AThread Thread, long Position)
{ {
do do
@ -70,7 +64,7 @@ namespace ChocolArm64
Position = Sub.Execute(Thread.ThreadState, Thread.Memory); Position = Sub.Execute(Thread.ThreadState, Thread.Memory);
} }
while (Position != 0 && KeepRunning); while (Position != 0 && Thread.ThreadState.Running);
} }
internal bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub) internal bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub)

View file

@ -34,6 +34,22 @@ namespace ChocolArm64.Instruction
Context.EmitCall(MthdInfo); Context.EmitCall(MthdInfo);
//Check if the thread should still be running, if it isn't then we return 0
//to force a return to the dispatcher and then exit the thread.
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Running));
AILLabel LblEnd = new AILLabel();
Context.Emit(OpCodes.Brtrue_S, LblEnd);
Context.EmitLdc_I8(0);
Context.Emit(OpCodes.Ret);
Context.MarkLabel(LblEnd);
if (Context.CurrBlock.Next != null) if (Context.CurrBlock.Next != null)
{ {
Context.EmitLoadState(Context.CurrBlock.Next); Context.EmitLoadState(Context.CurrBlock.Next);

View file

@ -29,6 +29,8 @@ namespace ChocolArm64.State
public int ProcessId; public int ProcessId;
public int ThreadId; public int ThreadId;
public bool Running { get; set; }
public long TpidrEl0 { get; set; } public long TpidrEl0 { get; set; }
public long Tpidr { get; set; } public long Tpidr { get; set; }

View file

@ -63,17 +63,13 @@ namespace Ryujinx.Core.Input
private object ShMemLock; private object ShMemLock;
private long[] ShMemPositions; private (AMemory, long)[] ShMemPositions;
private AMemory Memory; public Hid()
public Hid(AMemory Memory)
{ {
this.Memory = Memory;
ShMemLock = new object(); ShMemLock = new object();
ShMemPositions = new long[0]; ShMemPositions = new (AMemory, long)[0];
} }
internal void ShMemMap(object sender, EventArgs e) internal void ShMemMap(object sender, EventArgs e)
@ -84,11 +80,11 @@ namespace Ryujinx.Core.Input
{ {
ShMemPositions = SharedMem.GetVirtualPositions(); ShMemPositions = SharedMem.GetVirtualPositions();
long BasePosition = ShMemPositions[ShMemPositions.Length - 1]; (AMemory Memory, long Position) ShMem = ShMemPositions[ShMemPositions.Length - 1];
Logging.Info($"HID shared memory successfully mapped to 0x{BasePosition:x16}!"); Logging.Info($"HID shared memory successfully mapped to 0x{ShMem.Position:x16}!");
Init(BasePosition); Init(ShMem.Memory, ShMem.Position);
} }
} }
@ -102,10 +98,11 @@ namespace Ryujinx.Core.Input
} }
} }
private void Init(long BasePosition) private void Init(AMemory Memory, long Position)
{ {
InitializeJoyconPair( InitializeJoyconPair(
BasePosition, Memory,
Position,
JoyConColor.Body_Neon_Red, JoyConColor.Body_Neon_Red,
JoyConColor.Buttons_Neon_Red, JoyConColor.Buttons_Neon_Red,
JoyConColor.Body_Neon_Blue, JoyConColor.Body_Neon_Blue,
@ -113,13 +110,14 @@ namespace Ryujinx.Core.Input
} }
private void InitializeJoyconPair( private void InitializeJoyconPair(
long BasePosition, AMemory Memory,
long Position,
JoyConColor LeftColorBody, JoyConColor LeftColorBody,
JoyConColor LeftColorButtons, JoyConColor LeftColorButtons,
JoyConColor RightColorBody, JoyConColor RightColorBody,
JoyConColor RightColorButtons) JoyConColor RightColorButtons)
{ {
long BaseControllerOffset = BasePosition + HidControllersOffset + 8 * HidControllerSize; long BaseControllerOffset = Position + HidControllersOffset + 8 * HidControllerSize;
HidControllerType Type = HidControllerType Type =
HidControllerType.ControllerType_Handheld | HidControllerType.ControllerType_Handheld |
@ -160,7 +158,7 @@ namespace Ryujinx.Core.Input
{ {
lock (ShMemLock) lock (ShMemLock)
{ {
foreach (long Position in ShMemPositions) foreach ((AMemory Memory, long Position) in ShMemPositions)
{ {
long ControllerOffset = Position + HidControllersOffset; long ControllerOffset = Position + HidControllersOffset;
@ -207,7 +205,7 @@ namespace Ryujinx.Core.Input
{ {
lock (ShMemLock) lock (ShMemLock)
{ {
foreach (long Position in ShMemPositions) foreach ((AMemory Memory, long Position) in ShMemPositions)
{ {
long TouchScreenOffset = Position + HidTouchScreenOffset; long TouchScreenOffset = Position + HidTouchScreenOffset;

View file

@ -8,7 +8,8 @@ namespace Ryujinx.Core
{ {
public static class Logging public static class Logging
{ {
private static Stopwatch ExecutionTime = new Stopwatch(); private static Stopwatch ExecutionTime;
private const string LogFileName = "Ryujinx.log"; private const string LogFileName = "Ryujinx.log";
private static bool EnableInfo = Config.LoggingEnableInfo; private static bool EnableInfo = Config.LoggingEnableInfo;
@ -23,6 +24,10 @@ namespace Ryujinx.Core
static Logging() static Logging()
{ {
if (File.Exists(LogFileName)) File.Delete(LogFileName); if (File.Exists(LogFileName)) File.Delete(LogFileName);
ExecutionTime = new Stopwatch();
ExecutionTime.Start();
} }
public static string GetExecutionTime() public static string GetExecutionTime()

View file

@ -4,7 +4,7 @@ using System.Threading;
namespace Ryujinx.Core.OsHle namespace Ryujinx.Core.OsHle
{ {
public class CondVar class CondVar
{ {
private Process Process; private Process Process;

View file

@ -1,58 +1,48 @@
using Ryujinx.Core.OsHle.Utilities;
using System; using System;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Handles namespace Ryujinx.Core.OsHle.Handles
{ {
class HDomain : HSession class HDomain : HSession, IDisposable
{ {
private Dictionary<int, object> Objects; private IdDictionary Objects;
private IdPool ObjIds;
public HDomain(HSession Session) : base(Session) public HDomain(HSession Session) : base(Session)
{ {
Objects = new Dictionary<int, object>(); Objects = new IdDictionary();
ObjIds = new IdPool();
} }
public int GenerateObjectId(object Obj) public int Add(object Obj)
{ {
int Id = ObjIds.GenerateId(); return Objects.Add(Obj);
if (Id == -1)
{
throw new InvalidOperationException();
}
Objects.Add(Id, Obj);
return Id;
} }
public void DeleteObject(int Id) public bool Delete(int Id)
{ {
if (Objects.TryGetValue(Id, out object Obj)) return Objects.Delete(Id);
{
if (Obj is IDisposable DisposableObj)
{
DisposableObj.Dispose();
}
ObjIds.DeleteId(Id);
Objects.Remove(Id);
}
} }
public object GetObject(int Id) public object GetObject(int Id)
{ {
if (Objects.TryGetValue(Id, out object Obj)) return Objects.GetData(Id);
{ }
return Obj;
}
return null; public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{
foreach (object Obj in Objects)
{
if (Obj is IDisposable DisposableObj)
{
DisposableObj.Dispose();
}
}
}
} }
} }
} }

View file

@ -1,18 +0,0 @@
namespace Ryujinx.Core.OsHle.Handles
{
class HNvMap
{
public int Id { get; private set; }
public int Size { get; private set; }
public int Align { get; set; }
public int Kind { get; set; }
public long Address { get; set; }
public HNvMap(int Id, int Size)
{
this.Id = Id;
this.Size = Size;
}
}
}

View file

@ -1,3 +1,4 @@
using ChocolArm64.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -5,37 +6,37 @@ namespace Ryujinx.Core.OsHle.Handles
{ {
class HSharedMem class HSharedMem
{ {
private List<long> Positions; private List<(AMemory, long)> Positions;
public EventHandler<EventArgs> MemoryMapped; public EventHandler<EventArgs> MemoryMapped;
public EventHandler<EventArgs> MemoryUnmapped; public EventHandler<EventArgs> MemoryUnmapped;
public HSharedMem() public HSharedMem()
{ {
Positions = new List<long>(); Positions = new List<(AMemory, long)>();
} }
public void AddVirtualPosition(long Position) public void AddVirtualPosition(AMemory Memory, long Position)
{ {
lock (Positions) lock (Positions)
{ {
Positions.Add(Position); Positions.Add((Memory, Position));
MemoryMapped?.Invoke(this, EventArgs.Empty); MemoryMapped?.Invoke(this, EventArgs.Empty);
} }
} }
public void RemoveVirtualPosition(long Position) public void RemoveVirtualPosition(AMemory Memory, long Position)
{ {
lock (Positions) lock (Positions)
{ {
Positions.Remove(Position); Positions.Remove((Memory, Position));
MemoryUnmapped?.Invoke(this, EventArgs.Empty); MemoryUnmapped?.Invoke(this, EventArgs.Empty);
} }
} }
public long[] GetVirtualPositions() public (AMemory, long)[] GetVirtualPositions()
{ {
return Positions.ToArray(); return Positions.ToArray();
} }

View file

@ -0,0 +1,63 @@
using System;
namespace Ryujinx.Core.OsHle.Handles
{
class KProcessHandleTable : IDisposable
{
private IdDictionary Handles;
public KProcessHandleTable()
{
Handles = new IdDictionary();
}
public int OpenHandle(object Obj)
{
return Handles.Add(Obj);
}
public T GetData<T>(int Handle)
{
return Handles.GetData<T>(Handle);
}
public bool ReplaceData(int Id, object Data)
{
return Handles.ReplaceData(Id, Data);
}
public bool CloseHandle(int Handle)
{
object Data = Handles.GetData(Handle);
if (Data is HTransferMem TMem)
{
TMem.Memory.Manager.Reprotect(
TMem.Position,
TMem.Size,
TMem.Perm);
}
return Handles.Delete(Handle);
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{
foreach (object Obj in Handles)
{
if (Obj is IDisposable DisposableObj)
{
DisposableObj.Dispose();
}
}
}
}
}
}

View file

@ -37,5 +37,33 @@ namespace Ryujinx.Core.OsHle
Position += 0x18; Position += 0x18;
} }
public static string ReadHbAbiNextLoadPath(AMemory Memory, long Position)
{
string FileName = null;
while (true)
{
long Key = Memory.ReadInt64(Position);
if (Key == 2)
{
long Value0 = Memory.ReadInt64(Position + 0x08);
long Value1 = Memory.ReadInt64(Position + 0x10);
FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, (int)(Value1 - Value0));
break;
}
else if (Key == 0)
{
break;
}
Position += 0x18;
}
return FileName;
}
} }
} }

View file

@ -1,33 +1,23 @@
using Ryujinx.Core.Loaders.Executables; using Ryujinx.Core.Loaders.Executables;
using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Utilities;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.IO; using System.IO;
namespace Ryujinx.Core.OsHle namespace Ryujinx.Core.OsHle
{ {
public class Horizon public class Horizon : IDisposable
{ {
internal const int HidSize = 0x40000; internal const int HidSize = 0x40000;
internal const int FontSize = 0x50; internal const int FontSize = 0x50;
internal int HidHandle { get; private set; } internal ConcurrentDictionary<long, Mutex> Mutexes { get; private set; }
internal int FontHandle { get; private set; } internal ConcurrentDictionary<long, CondVar> CondVars { get; private set; }
internal IdPool IdGen { get; private set; }
internal IdPool NvMapIds { get; private set; }
internal IdPoolWithObj Handles { get; private set; }
internal IdPoolWithObj Fds { get; private set; }
internal IdPoolWithObj Displays { get; private set; }
public ConcurrentDictionary<long, Mutex> Mutexes { get; private set; }
public ConcurrentDictionary<long, CondVar> CondVars { get; private set; }
private ConcurrentDictionary<int, Process> Processes; private ConcurrentDictionary<int, Process> Processes;
internal HSharedMem HidSharedMem; internal HSharedMem HidSharedMem;
internal HSharedMem FontSharedMem;
private Switch Ns; private Switch Ns;
@ -35,25 +25,13 @@ namespace Ryujinx.Core.OsHle
{ {
this.Ns = Ns; this.Ns = Ns;
IdGen = new IdPool();
NvMapIds = new IdPool();
Handles = new IdPoolWithObj();
Fds = new IdPoolWithObj();
Displays = new IdPoolWithObj();
Mutexes = new ConcurrentDictionary<long, Mutex>(); Mutexes = new ConcurrentDictionary<long, Mutex>();
CondVars = new ConcurrentDictionary<long, CondVar>(); CondVars = new ConcurrentDictionary<long, CondVar>();
Processes = new ConcurrentDictionary<int, Process>(); Processes = new ConcurrentDictionary<int, Process>();
HidSharedMem = new HSharedMem(); HidSharedMem = new HSharedMem();
FontSharedMem = new HSharedMem();
HidHandle = Handles.GenerateId(HidSharedMem);
FontHandle = Handles.GenerateId(new HSharedMem());
HidSharedMem.AddVirtualPosition(0);
} }
public void LoadCart(string ExeFsDir, string RomFsFile = null) public void LoadCart(string ExeFsDir, string RomFsFile = null)
@ -63,9 +41,7 @@ namespace Ryujinx.Core.OsHle
Ns.VFs.LoadRomFs(RomFsFile); Ns.VFs.LoadRomFs(RomFsFile);
} }
int ProcessId = IdGen.GenerateId(); Process MainProcess = MakeProcess();
Process MainProcess = new Process(Ns, ProcessId);
void LoadNso(string FileName) void LoadNso(string FileName)
{ {
@ -96,17 +72,13 @@ namespace Ryujinx.Core.OsHle
LoadNso("sdk"); LoadNso("sdk");
MainProcess.Run(); MainProcess.Run();
Processes.TryAdd(ProcessId, MainProcess);
} }
public void LoadProgram(string FileName) public void LoadProgram(string FileName)
{ {
bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro"; bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro";
int ProcessId = IdGen.GenerateId(); Process MainProcess = MakeProcess();
Process MainProcess = new Process(Ns, ProcessId);
using (FileStream Input = new FileStream(FileName, FileMode.Open)) using (FileStream Input = new FileStream(FileName, FileMode.Open))
{ {
@ -117,34 +89,67 @@ namespace Ryujinx.Core.OsHle
MainProcess.SetEmptyArgs(); MainProcess.SetEmptyArgs();
MainProcess.Run(IsNro); MainProcess.Run(IsNro);
Processes.TryAdd(ProcessId, MainProcess);
} }
public void FinalizeAllProcesses() private Process MakeProcess()
{ {
foreach (Process Process in Processes.Values) Process Process;
lock (Processes)
{ {
Process.StopAllThreads(); int ProcessId = 0;
while (Processes.ContainsKey(ProcessId))
{
ProcessId++;
}
Process = new Process(Ns, ProcessId);
Processes.TryAdd(ProcessId, Process);
}
return Process;
}
internal void ExitProcess(int ProcessId)
{
if (Processes.TryGetValue(ProcessId, out Process Process) && Process.NeedsHbAbi)
{
string NextNro = Homebrew.ReadHbAbiNextLoadPath(Process.Memory, Process.HbAbiDataPosition);
Logging.Info($"HbAbi NextLoadPath {NextNro}");
if (NextNro == string.Empty)
{
NextNro = "sdmc:/hbmenu.nro";
}
NextNro = NextNro.Replace("sdmc:", string.Empty);
NextNro = Ns.VFs.GetFullPath(Ns.VFs.GetSdCardPath(), NextNro);
if (File.Exists(NextNro))
{
//TODO: Those dictionaries shouldn't even exist,
//the Mutex and CondVar helper classes should be static.
Mutexes.Clear();
CondVars.Clear();
LoadProgram(NextNro);
}
}
if (Processes.TryRemove(ProcessId, out Process))
{
Process.StopAllThreadsAsync();
Process.Dispose(); Process.Dispose();
}
}
internal bool ExitProcess(int ProcessId) if (Processes.Count == 0)
{ {
bool Success = Processes.TryRemove(ProcessId, out Process Process); Ns.OnFinish(EventArgs.Empty);
}
if (Success)
{
Process.StopAllThreads();
} }
if (Processes.Count == 0)
{
Ns.OnFinish(EventArgs.Empty);
}
return Success;
} }
internal bool TryGetProcess(int ProcessId, out Process Process) internal bool TryGetProcess(int ProcessId, out Process Process)
@ -152,19 +157,21 @@ namespace Ryujinx.Core.OsHle
return Processes.TryGetValue(ProcessId, out Process); return Processes.TryGetValue(ProcessId, out Process);
} }
internal void CloseHandle(int Handle) public void Dispose()
{ {
object HndData = Handles.GetData<object>(Handle); Dispose(true);
}
if (HndData is HTransferMem TransferMem) protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{ {
TransferMem.Memory.Manager.Reprotect( foreach (Process Process in Processes.Values)
TransferMem.Position, {
TransferMem.Size, Process.StopAllThreadsAsync();
TransferMem.Perm); Process.Dispose();
}
} }
Handles.Delete(Handle);
} }
} }
} }

View file

@ -3,31 +3,40 @@ using System.Collections;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Utilities namespace Ryujinx.Core.OsHle
{ {
class IdPoolWithObj : IEnumerable<KeyValuePair<int, object>> class IdDictionary : IEnumerable<object>
{ {
private IdPool Ids;
private ConcurrentDictionary<int, object> Objs; private ConcurrentDictionary<int, object> Objs;
public IdPoolWithObj() private int FreeIdHint = 1;
{
Ids = new IdPool();
public IdDictionary()
{
Objs = new ConcurrentDictionary<int, object>(); Objs = new ConcurrentDictionary<int, object>();
} }
public int GenerateId(object Data) public int Add(object Data)
{ {
int Id = Ids.GenerateId(); if (Objs.TryAdd(FreeIdHint, Data))
if (Id == -1 || !Objs.TryAdd(Id, Data))
{ {
throw new InvalidOperationException(); return FreeIdHint++;
} }
return Id; return AddSlow(Data);
}
private int AddSlow(object Data)
{
for (int Id = 1; Id < int.MaxValue; Id++)
{
if (Objs.TryAdd(Id, Data))
{
return Id;
}
}
throw new InvalidOperationException();
} }
public bool ReplaceData(int Id, object Data) public bool ReplaceData(int Id, object Data)
@ -42,6 +51,16 @@ namespace Ryujinx.Core.OsHle.Utilities
return false; return false;
} }
public object GetData(int Id)
{
if (Objs.TryGetValue(Id, out object Data))
{
return Data;
}
return null;
}
public T GetData<T>(int Id) public T GetData<T>(int Id)
{ {
if (Objs.TryGetValue(Id, out object Data) && Data is T) if (Objs.TryGetValue(Id, out object Data) && Data is T)
@ -52,7 +71,7 @@ namespace Ryujinx.Core.OsHle.Utilities
return default(T); return default(T);
} }
public void Delete(int Id) public bool Delete(int Id)
{ {
if (Objs.TryRemove(Id, out object Obj)) if (Objs.TryRemove(Id, out object Obj))
{ {
@ -61,18 +80,22 @@ namespace Ryujinx.Core.OsHle.Utilities
DisposableObj.Dispose(); DisposableObj.Dispose();
} }
Ids.DeleteId(Id); FreeIdHint = Id;
return true;
} }
return false;
} }
IEnumerator<KeyValuePair<int, object>> IEnumerable<KeyValuePair<int, object>>.GetEnumerator() IEnumerator<object> IEnumerable<object>.GetEnumerator()
{ {
return Objs.GetEnumerator(); return Objs.Values.GetEnumerator();
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
return Objs.GetEnumerator(); return Objs.Values.GetEnumerator();
} }
} }
} }

View file

@ -13,6 +13,7 @@ namespace Ryujinx.Core.OsHle.Ipc
public static void IpcCall( public static void IpcCall(
Switch Ns, Switch Ns,
Process Process,
AMemory Memory, AMemory Memory,
HSession Session, HSession Session,
IpcMessage Request, IpcMessage Request,
@ -60,7 +61,7 @@ namespace Ryujinx.Core.OsHle.Ipc
} }
else if (Request.DomCmd == IpcDomCmd.DeleteObj) else if (Request.DomCmd == IpcDomCmd.DeleteObj)
{ {
Dom.DeleteObject(Request.DomObjId); Dom.Delete(Request.DomObjId);
Response = FillResponse(Response, 0); Response = FillResponse(Response, 0);
@ -100,6 +101,7 @@ namespace Ryujinx.Core.OsHle.Ipc
ServiceCtx Context = new ServiceCtx( ServiceCtx Context = new ServiceCtx(
Ns, Ns,
Process,
Memory, Memory,
Session, Session,
Request, Request,
@ -124,15 +126,43 @@ namespace Ryujinx.Core.OsHle.Ipc
switch (CmdId) switch (CmdId)
{ {
case 0: Request = IpcConvertSessionToDomain(Ns, Session, Response, HndId); break; case 0:
case 3: Request = IpcQueryBufferPointerSize(Response); break; {
case 2: //IpcDuplicateSession, differences is unknown. HDomain Dom = new HDomain(Session);
case 4: Request = IpcDuplicateSessionEx(Ns, Session, Response, ReqReader); break;
Process.HandleTable.ReplaceData(HndId, Dom);
Request = FillResponse(Response, 0, Dom.Add(Dom));
break;
}
case 3:
{
Request = FillResponse(Response, 0, 0x500);
break;
}
//TODO: Whats the difference between IpcDuplicateSession/Ex?
case 2:
case 4:
{
int Unknown = ReqReader.ReadInt32();
int Handle = Process.HandleTable.OpenHandle(Session);
Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
Request = FillResponse(Response, 0);
break;
}
default: throw new NotImplementedException(CmdId.ToString()); default: throw new NotImplementedException(CmdId.ToString());
} }
} }
else if (Request.Type == IpcMessageType.Unknown2) else if (Request.Type == IpcMessageType.CloseSession)
{ {
//TODO //TODO
} }
@ -145,39 +175,6 @@ namespace Ryujinx.Core.OsHle.Ipc
} }
} }
private static IpcMessage IpcConvertSessionToDomain(
Switch Ns,
HSession Session,
IpcMessage Response,
int HndId)
{
HDomain Dom = new HDomain(Session);
Ns.Os.Handles.ReplaceData(HndId, Dom);
return FillResponse(Response, 0, Dom.GenerateObjectId(Dom));
}
private static IpcMessage IpcDuplicateSessionEx(
Switch Ns,
HSession Session,
IpcMessage Response,
BinaryReader ReqReader)
{
int Unknown = ReqReader.ReadInt32();
int Handle = Ns.Os.Handles.GenerateId(Session);
Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
return FillResponse(Response, 0);
}
private static IpcMessage IpcQueryBufferPointerSize(IpcMessage Response)
{
return FillResponse(Response, 0, 0x500);
}
private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values) private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values)
{ {
using (MemoryStream MS = new MemoryStream()) using (MemoryStream MS = new MemoryStream())

View file

@ -2,9 +2,9 @@ namespace Ryujinx.Core.OsHle.Ipc
{ {
enum IpcMessageType enum IpcMessageType
{ {
Response = 0, Response = 0,
Unknown2 = 2, CloseSession = 2,
Request = 4, Request = 4,
Control = 5 Control = 5
} }
} }

View file

@ -4,7 +4,7 @@ using System.Threading;
namespace Ryujinx.Core.OsHle namespace Ryujinx.Core.OsHle
{ {
public class Mutex class Mutex
{ {
private const int MutexHasListenersMask = 0x40000000; private const int MutexHasListenersMask = 0x40000000;

View file

@ -9,25 +9,32 @@ using Ryujinx.Core.OsHle.Svc;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.Core.OsHle namespace Ryujinx.Core.OsHle
{ {
public class Process : IDisposable class Process : IDisposable
{ {
private const int TlsSize = 0x200; private const int TlsSize = 0x200;
private const int TotalTlsSlots = 32; private const int TotalTlsSlots = 32;
private Switch Ns; private Switch Ns;
public bool NeedsHbAbi { get; private set; }
public long HbAbiDataPosition { get; private set; }
public int ProcessId { get; private set; } public int ProcessId { get; private set; }
private ATranslator Translator; private ATranslator Translator;
public AMemory Memory { get; private set; } public AMemory Memory { get; private set; }
public ServiceMgr Services { get; private set; }
public KProcessScheduler Scheduler { get; private set; } public KProcessScheduler Scheduler { get; private set; }
public KProcessHandleTable HandleTable { get; private set; }
private SvcHandler SvcHandler; private SvcHandler SvcHandler;
private ConcurrentDictionary<int, AThread> TlsSlots; private ConcurrentDictionary<int, AThread> TlsSlots;
@ -40,14 +47,22 @@ namespace Ryujinx.Core.OsHle
private long ImageBase; private long ImageBase;
private bool ShouldDispose;
private bool Disposed;
public Process(Switch Ns, int ProcessId) public Process(Switch Ns, int ProcessId)
{ {
this.Ns = Ns; this.Ns = Ns;
this.ProcessId = ProcessId; this.ProcessId = ProcessId;
Memory = Ns.Memory; Memory = new AMemory();
Scheduler = new KProcessScheduler(); Services = new ServiceMgr();
HandleTable = new KProcessHandleTable();
Scheduler = new KProcessScheduler();
SvcHandler = new SvcHandler(Ns, this); SvcHandler = new SvcHandler(Ns, this);
@ -67,6 +82,11 @@ namespace Ryujinx.Core.OsHle
public void LoadProgram(IExecutable Program) public void LoadProgram(IExecutable Program)
{ {
if (Disposed)
{
throw new ObjectDisposedException(nameof(Process));
}
Logging.Info($"Image base at 0x{ImageBase:x16}."); Logging.Info($"Image base at 0x{ImageBase:x16}.");
Executable Executable = new Executable(Program, Memory, ImageBase); Executable Executable = new Executable(Program, Memory, ImageBase);
@ -78,11 +98,19 @@ namespace Ryujinx.Core.OsHle
public void SetEmptyArgs() public void SetEmptyArgs()
{ {
//TODO: This should be part of Run.
ImageBase += AMemoryMgr.PageSize; ImageBase += AMemoryMgr.PageSize;
} }
public bool Run(bool UseHbAbi = false) public bool Run(bool NeedsHbAbi = false)
{ {
if (Disposed)
{
throw new ObjectDisposedException(nameof(Process));
}
this.NeedsHbAbi = NeedsHbAbi;
if (Executables.Count == 0) if (Executables.Count == 0)
{ {
return false; return false;
@ -102,11 +130,11 @@ namespace Ryujinx.Core.OsHle
return false; return false;
} }
MainThread = Ns.Os.Handles.GetData<HThread>(Handle); MainThread = HandleTable.GetData<HThread>(Handle);
if (UseHbAbi) if (NeedsHbAbi)
{ {
long HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd); HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd);
Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle); Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle);
@ -124,22 +152,21 @@ namespace Ryujinx.Core.OsHle
Memory.Manager.Map(Position, Size, (int)Type, AMemoryPerm.RW); Memory.Manager.Map(Position, Size, (int)Type, AMemoryPerm.RW);
} }
public void StopAllThreads() public void StopAllThreadsAsync()
{ {
if (Disposed)
{
throw new ObjectDisposedException(nameof(Process));
}
if (MainThread != null) if (MainThread != null)
{ {
while (MainThread.Thread.IsAlive) MainThread.Thread.StopExecution();
{
MainThread.Thread.StopExecution();
}
} }
foreach (AThread Thread in TlsSlots.Values) foreach (AThread Thread in TlsSlots.Values)
{ {
while (Thread.IsAlive) Thread.StopExecution();
{
Thread.StopExecution();
}
} }
} }
@ -150,49 +177,26 @@ namespace Ryujinx.Core.OsHle
int Priority, int Priority,
int ProcessorId) int ProcessorId)
{ {
ThreadPriority ThreadPrio; if (Disposed)
if (Priority < 12)
{ {
ThreadPrio = ThreadPriority.Highest; throw new ObjectDisposedException(nameof(Process));
}
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(GetTranslator(), Memory, ThreadPrio, EntryPoint); AThread Thread = new AThread(GetTranslator(), Memory, EntryPoint);
HThread ThreadHnd = new HThread(Thread, ProcessorId, Priority); HThread ThreadHnd = new HThread(Thread, ProcessorId, Priority);
int Handle = Ns.Os.Handles.GenerateId(ThreadHnd); int Handle = HandleTable.OpenHandle(ThreadHnd);
int TlsSlot = GetFreeTlsSlot(Thread); int ThreadId = GetFreeTlsSlot(Thread);
if (TlsSlot == -1 || Handle == -1) long Tpidr = MemoryRegions.TlsPagesAddress + ThreadId * TlsSize;
{
return -1;
}
long Tpidr = MemoryRegions.TlsPagesAddress + TlsSlot * TlsSize;
Thread.ThreadState.Break += BreakHandler; Thread.ThreadState.Break += BreakHandler;
Thread.ThreadState.SvcCall += SvcHandler.SvcCall; Thread.ThreadState.SvcCall += SvcHandler.SvcCall;
Thread.ThreadState.Undefined += UndefinedHandler; Thread.ThreadState.Undefined += UndefinedHandler;
Thread.ThreadState.ProcessId = ProcessId; Thread.ThreadState.ProcessId = ProcessId;
Thread.ThreadState.ThreadId = Ns.Os.IdGen.GenerateId(); Thread.ThreadState.ThreadId = ThreadId;
Thread.ThreadState.Tpidr = Tpidr; Thread.ThreadState.Tpidr = Tpidr;
Thread.ThreadState.X0 = (ulong)ArgsPtr; Thread.ThreadState.X0 = (ulong)ArgsPtr;
Thread.ThreadState.X1 = (ulong)Handle; Thread.ThreadState.X1 = (ulong)Handle;
@ -224,7 +228,7 @@ namespace Ryujinx.Core.OsHle
foreach (Executable Exe in Executables) foreach (Executable Exe in Executables)
{ {
foreach (KeyValuePair<long, string> KV in Exe.SymbolTable) foreach (KeyValuePair<long, string> KV in Exe.SymbolTable)
{ {
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value); SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
} }
} }
@ -274,16 +278,28 @@ namespace Ryujinx.Core.OsHle
} }
} }
return -1; throw new InvalidOperationException();
} }
private void ThreadFinished(object sender, EventArgs e) private void ThreadFinished(object sender, EventArgs e)
{ {
if (sender is AThread Thread) if (sender is AThread Thread)
{ {
TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _); Logging.Info($"Thread {Thread.ThreadId} exiting...");
Ns.Os.IdGen.DeleteId(Thread.ThreadId); TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _);
}
if (TlsSlots.Count == 0)
{
if (ShouldDispose)
{
Dispose();
}
Logging.Info($"No threads running, now exiting Process {ProcessId}...");
Ns.Os.ExitProcess(ProcessId);
} }
} }
@ -309,9 +325,30 @@ namespace Ryujinx.Core.OsHle
protected virtual void Dispose(bool Disposing) protected virtual void Dispose(bool Disposing)
{ {
if (Disposing) if (Disposing && !Disposed)
{ {
//If there is still some thread running, disposing the objects is not
//safe as the thread may try to access those resources. Instead, we set
//the flag to have the Process disposed when all threads finishes.
//Note: This may not happen if the guest code gets stuck on a infinite loop.
if (TlsSlots.Count > 0)
{
ShouldDispose = true;
Logging.Info($"Process {ProcessId} waiting all threads terminate...");
return;
}
Disposed = true;
Services.Dispose();
HandleTable.Dispose();
Scheduler.Dispose(); Scheduler.Dispose();
SvcHandler.Dispose();
Memory.Dispose();
Logging.Info($"Process {ProcessId} exiting...");
} }
} }
} }

View file

@ -8,6 +8,7 @@ namespace Ryujinx.Core.OsHle
class ServiceCtx class ServiceCtx
{ {
public Switch Ns { get; private set; } public Switch Ns { get; private set; }
public Process Process { get; private set; }
public AMemory Memory { get; private set; } public AMemory Memory { get; private set; }
public HSession Session { get; private set; } public HSession Session { get; private set; }
public IpcMessage Request { get; private set; } public IpcMessage Request { get; private set; }
@ -17,6 +18,7 @@ namespace Ryujinx.Core.OsHle
public ServiceCtx( public ServiceCtx(
Switch Ns, Switch Ns,
Process Process,
AMemory Memory, AMemory Memory,
HSession Session, HSession Session,
IpcMessage Request, IpcMessage Request,
@ -25,6 +27,7 @@ namespace Ryujinx.Core.OsHle
BinaryWriter ResponseData) BinaryWriter ResponseData)
{ {
this.Ns = Ns; this.Ns = Ns;
this.Process = Process;
this.Memory = Memory; this.Memory = Memory;
this.Session = Session; this.Session = Session;
this.Request = Request; this.Request = Request;

View file

@ -0,0 +1,109 @@
using Ryujinx.Core.OsHle.IpcServices;
using Ryujinx.Core.OsHle.IpcServices.Acc;
using Ryujinx.Core.OsHle.IpcServices.Am;
using Ryujinx.Core.OsHle.IpcServices.Apm;
using Ryujinx.Core.OsHle.IpcServices.Aud;
using Ryujinx.Core.OsHle.IpcServices.Bsd;
using Ryujinx.Core.OsHle.IpcServices.Friend;
using Ryujinx.Core.OsHle.IpcServices.FspSrv;
using Ryujinx.Core.OsHle.IpcServices.Hid;
using Ryujinx.Core.OsHle.IpcServices.Lm;
using Ryujinx.Core.OsHle.IpcServices.Nifm;
using Ryujinx.Core.OsHle.IpcServices.Ns;
using Ryujinx.Core.OsHle.IpcServices.NvServices;
using Ryujinx.Core.OsHle.IpcServices.Pctl;
using Ryujinx.Core.OsHle.IpcServices.Pl;
using Ryujinx.Core.OsHle.IpcServices.Set;
using Ryujinx.Core.OsHle.IpcServices.Sfdnsres;
using Ryujinx.Core.OsHle.IpcServices.Sm;
using Ryujinx.Core.OsHle.IpcServices.Ssl;
using Ryujinx.Core.OsHle.IpcServices.Time;
using Ryujinx.Core.OsHle.IpcServices.Vi;
using System;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle
{
class ServiceMgr : IDisposable
{
private Dictionary<string, IIpcService> Services;
public ServiceMgr()
{
Services = new Dictionary<string, IIpcService>();
}
public IIpcService GetService(string Name)
{
lock (Services)
{
if (!Services.TryGetValue(Name, out IIpcService Service))
{
switch (Name)
{
case "acc:u0": Service = new ServiceAcc(); break;
case "aoc:u": Service = new ServiceNs(); break;
case "apm": Service = new ServiceApm(); break;
case "apm:p": Service = new ServiceApm(); break;
case "appletOE": Service = new ServiceAppletOE(); break;
case "audout:u": Service = new ServiceAudOut(); break;
case "audren:u": Service = new ServiceAudRen(); break;
case "bsd:u": Service = new ServiceBsd(); break;
case "friend:a": Service = new ServiceFriend(); break;
case "fsp-srv": Service = new ServiceFspSrv(); break;
case "hid": Service = new ServiceHid(); break;
case "lm": Service = new ServiceLm(); break;
case "nifm:u": Service = new ServiceNifm(); break;
case "nvdrv": Service = new ServiceNvDrv(); break;
case "nvdrv:a": Service = new ServiceNvDrv(); break;
case "pctl:a": Service = new ServicePctl(); break;
case "pl:u": Service = new ServicePl(); break;
case "set": Service = new ServiceSet(); break;
case "set:sys": Service = new ServiceSetSys(); break;
case "sfdnsres": Service = new ServiceSfdnsres(); break;
case "sm:": Service = new ServiceSm(); break;
case "ssl": Service = new ServiceSsl(); break;
case "time:s": Service = new ServiceTime(); break;
case "time:u": Service = new ServiceTime(); break;
case "vi:m": Service = new ServiceVi(); break;
case "vi:s": Service = new ServiceVi(); break;
case "vi:u": Service = new ServiceVi(); break;
}
if (Service == null)
{
throw new NotImplementedException(Name);
}
Services.Add(Name, Service);
}
return Service;
}
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{
lock (Services)
{
foreach (IIpcService Service in Services.Values)
{
if (Service is IDisposable DisposableSrv)
{
DisposableSrv.Dispose();
}
}
Services.Clear();
}
}
}
}
}

View file

@ -139,7 +139,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
public long RegisterBufferEvent(ServiceCtx Context) public long RegisterBufferEvent(ServiceCtx Context)
{ {
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent()); int Handle = Context.Process.HandleTable.OpenHandle(new HEvent());
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);

View file

@ -56,7 +56,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
public long QuerySystemEvent(ServiceCtx Context) public long QuerySystemEvent(ServiceCtx Context)
{ {
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent()); int Handle = Context.Process.HandleTable.OpenHandle(new HEvent());
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);

View file

@ -10,21 +10,23 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private HSharedMem Handle; private HSharedMem HidSharedMem;
public IAppletResource(HSharedMem Handle) public IAppletResource(HSharedMem HidSharedMem)
{ {
m_Commands = new Dictionary<int, ServiceProcessRequest>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
{ {
{ 0, GetSharedMemoryHandle } { 0, GetSharedMemoryHandle }
}; };
this.Handle = Handle; this.HidSharedMem = HidSharedMem;
} }
public static long GetSharedMemoryHandle(ServiceCtx Context) public long GetSharedMemoryHandle(ServiceCtx Context)
{ {
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.HidHandle); int Handle = Context.Process.HandleTable.OpenHandle(HidSharedMem);
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
return 0; return 0;
} }

View file

@ -1,4 +1,3 @@
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc; using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic; using System.Collections.Generic;
@ -32,9 +31,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
public long CreateAppletResource(ServiceCtx Context) public long CreateAppletResource(ServiceCtx Context)
{ {
HSharedMem HidHndData = Context.Ns.Os.Handles.GetData<HSharedMem>(Context.Ns.Os.HidHandle); MakeObject(Context, new IAppletResource(Context.Ns.Os.HidSharedMem));
MakeObject(Context, new IAppletResource(HidHndData));
return 0; return 0;
} }

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Core.OsHle.IpcServices.NvServices
{
class NvFd
{
public string Name { get; private set; }
public NvFd(string Name)
{
this.Name = Name;
}
}
}

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Core.OsHle.IpcServices.NvServices
{
class NvMap
{
public int Handle;
public int Id;
public int Size;
public int Align;
public int Kind;
public long Address;
}
}

View file

@ -1,5 +1,4 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc; using Ryujinx.Core.OsHle.Ipc;
using Ryujinx.Core.OsHle.Utilities; using Ryujinx.Core.OsHle.Utilities;
using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu;
@ -12,41 +11,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
{ {
private delegate long ServiceProcessIoctl(ServiceCtx Context); private delegate long ServiceProcessIoctl(ServiceCtx Context);
private static Dictionary<(string, int), ServiceProcessIoctl> IoctlCmds =
new Dictionary<(string, int), ServiceProcessIoctl>()
{
{ ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel },
{ ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace },
{ ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx },
{ ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions },
{ ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx },
{ ("/dev/nvhost-ctrl", 0x001b), NvHostIoctlCtrlGetConfig },
{ ("/dev/nvhost-ctrl", 0x001d), NvHostIoctlCtrlEventWait },
{ ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize },
{ ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo },
{ ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics },
{ ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks },
{ ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask },
{ ("/dev/nvhost-gpu", 0x4714), NvMapIoctlChannelSetUserData },
{ ("/dev/nvhost-gpu", 0x4801), NvMapIoctlChannelSetNvMap },
{ ("/dev/nvhost-gpu", 0x4808), NvMapIoctlChannelSubmitGpFifo },
{ ("/dev/nvhost-gpu", 0x4809), NvMapIoctlChannelAllocObjCtx },
{ ("/dev/nvhost-gpu", 0x480b), NvMapIoctlChannelZcullBind },
{ ("/dev/nvhost-gpu", 0x480c), NvMapIoctlChannelSetErrorNotifier },
{ ("/dev/nvhost-gpu", 0x480d), NvMapIoctlChannelSetPriority },
{ ("/dev/nvhost-gpu", 0x481a), NvMapIoctlChannelAllocGpFifoEx2 },
{ ("/dev/nvmap", 0x0101), NvMapIocCreate },
{ ("/dev/nvmap", 0x0103), NvMapIocFromId },
{ ("/dev/nvmap", 0x0104), NvMapIocAlloc },
{ ("/dev/nvmap", 0x0105), NvMapIocFree },
{ ("/dev/nvmap", 0x0109), NvMapIocParam },
{ ("/dev/nvmap", 0x010e), NvMapIocGetId },
};
private Dictionary<int, ServiceProcessRequest> m_Commands; private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private Dictionary<(string, int), ServiceProcessIoctl> IoctlCmds;
private IdDictionary Fds;
private IdDictionary NvMaps;
private IdDictionary NvMapsById;
public ServiceNvDrv() public ServiceNvDrv()
{ {
m_Commands = new Dictionary<int, ServiceProcessRequest>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
@ -58,15 +33,50 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
{ 4, QueryEvent }, { 4, QueryEvent },
{ 8, SetClientPid }, { 8, SetClientPid },
}; };
IoctlCmds = new Dictionary<(string, int), ServiceProcessIoctl>()
{
{ ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel },
{ ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace },
{ ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx },
{ ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions },
{ ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx },
{ ("/dev/nvhost-ctrl", 0x001b), NvHostIoctlCtrlGetConfig },
{ ("/dev/nvhost-ctrl", 0x001d), NvHostIoctlCtrlEventWait },
{ ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize },
{ ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo },
{ ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics },
{ ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks },
{ ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask },
{ ("/dev/nvhost-gpu", 0x4714), NvMapIoctlChannelSetUserData },
{ ("/dev/nvhost-gpu", 0x4801), NvMapIoctlChannelSetNvMap },
{ ("/dev/nvhost-gpu", 0x4808), NvMapIoctlChannelSubmitGpFifo },
{ ("/dev/nvhost-gpu", 0x4809), NvMapIoctlChannelAllocObjCtx },
{ ("/dev/nvhost-gpu", 0x480b), NvMapIoctlChannelZcullBind },
{ ("/dev/nvhost-gpu", 0x480c), NvMapIoctlChannelSetErrorNotifier },
{ ("/dev/nvhost-gpu", 0x480d), NvMapIoctlChannelSetPriority },
{ ("/dev/nvhost-gpu", 0x481a), NvMapIoctlChannelAllocGpFifoEx2 },
{ ("/dev/nvmap", 0x0101), NvMapIocCreate },
{ ("/dev/nvmap", 0x0103), NvMapIocFromId },
{ ("/dev/nvmap", 0x0104), NvMapIocAlloc },
{ ("/dev/nvmap", 0x0105), NvMapIocFree },
{ ("/dev/nvmap", 0x0109), NvMapIocParam },
{ ("/dev/nvmap", 0x010e), NvMapIocGetId },
};
Fds = new IdDictionary();
NvMaps = new IdDictionary();
NvMapsById = new IdDictionary();
} }
public static long Open(ServiceCtx Context) public long Open(ServiceCtx Context)
{ {
long NamePtr = Context.Request.SendBuff[0].Position; long NamePtr = Context.Request.SendBuff[0].Position;
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, NamePtr); string Name = AMemoryHelper.ReadAsciiString(Context.Memory, NamePtr);
int Fd = Context.Ns.Os.Fds.GenerateId(new FileDesc(Name)); int Fd = Fds.Add(new NvFd(Name));
Context.ResponseData.Write(Fd); Context.ResponseData.Write(Fd);
Context.ResponseData.Write(0); Context.ResponseData.Write(0);
@ -74,12 +84,12 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
public static long Ioctl(ServiceCtx Context) public long Ioctl(ServiceCtx Context)
{ {
int Fd = Context.RequestData.ReadInt32(); int Fd = Context.RequestData.ReadInt32();
int Cmd = Context.RequestData.ReadInt32() & 0xffff; int Cmd = Context.RequestData.ReadInt32() & 0xffff;
FileDesc FdData = Context.Ns.Os.Fds.GetData<FileDesc>(Fd); NvFd FdData = Fds.GetData<NvFd>(Fd);
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -95,18 +105,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
} }
} }
public static long Close(ServiceCtx Context) public long Close(ServiceCtx Context)
{ {
int Fd = Context.RequestData.ReadInt32(); int Fd = Context.RequestData.ReadInt32();
Context.Ns.Os.Fds.Delete(Fd); Fds.Delete(Fd);
Context.ResponseData.Write(0); Context.ResponseData.Write(0);
return 0; return 0;
} }
public static long Initialize(ServiceCtx Context) public long Initialize(ServiceCtx Context)
{ {
long TransferMemSize = Context.RequestData.ReadInt64(); long TransferMemSize = Context.RequestData.ReadInt64();
int TransferMemHandle = Context.Request.HandleDesc.ToCopy[0]; int TransferMemHandle = Context.Request.HandleDesc.ToCopy[0];
@ -116,7 +126,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
public static long QueryEvent(ServiceCtx Context) public long QueryEvent(ServiceCtx Context)
{ {
int Fd = Context.RequestData.ReadInt32(); int Fd = Context.RequestData.ReadInt32();
int EventId = Context.RequestData.ReadInt32(); int EventId = Context.RequestData.ReadInt32();
@ -128,7 +138,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
public static long SetClientPid(ServiceCtx Context) public long SetClientPid(ServiceCtx Context)
{ {
long Pid = Context.RequestData.ReadInt64(); long Pid = Context.RequestData.ReadInt64();
@ -137,7 +147,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuAsIoctlBindChannel(ServiceCtx Context) private long NvGpuAsIoctlBindChannel(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -146,7 +156,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuAsIoctlAllocSpace(ServiceCtx Context) private long NvGpuAsIoctlAllocSpace(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -172,7 +182,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuAsIoctlMapBufferEx(ServiceCtx Context) private long NvGpuAsIoctlMapBufferEx(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -186,18 +196,29 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
long MapSize = Reader.ReadInt64(); long MapSize = Reader.ReadInt64();
long Offset = Reader.ReadInt64(); long Offset = Reader.ReadInt64();
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle); if (Handle == 0)
if (NvMap != null)
{ {
if ((Flags & 1) != 0) //Handle 0 is valid here, but it refers to something else.
{ //TODO: Figure out what, for now just return success.
Offset = Context.Ns.Gpu.MapMemory(NvMap.Address, Offset, NvMap.Size); return 0;
} }
else
{ NvMap Map = NvMaps.GetData<NvMap>(Handle);
Offset = Context.Ns.Gpu.MapMemory(NvMap.Address, NvMap.Size);
} if (Map == null)
{
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
return -1; //TODO: Corrent error code.
}
if ((Flags & 1) != 0)
{
Offset = Context.Ns.Gpu.MapMemory(Map.Address, Offset, Map.Size);
}
else
{
Offset = Context.Ns.Gpu.MapMemory(Map.Address, Map.Size);
} }
Context.Memory.WriteInt64(Position + 0x20, Offset); Context.Memory.WriteInt64(Position + 0x20, Offset);
@ -205,7 +226,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuAsIoctlGetVaRegions(ServiceCtx Context) private long NvGpuAsIoctlGetVaRegions(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -235,7 +256,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuAsIoctlInitializeEx(ServiceCtx Context) private long NvGpuAsIoctlInitializeEx(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -252,7 +273,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvHostIoctlCtrlGetConfig(ServiceCtx Context) private long NvHostIoctlCtrlGetConfig(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -267,7 +288,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvHostIoctlCtrlEventWait(ServiceCtx Context) private long NvHostIoctlCtrlEventWait(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -283,7 +304,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context) private long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -292,7 +313,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuIoctlZcullGetInfo(ServiceCtx Context) private long NvGpuIoctlZcullGetInfo(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -312,7 +333,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuIoctlGetCharacteristics(ServiceCtx Context) private long NvGpuIoctlGetCharacteristics(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -374,7 +395,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuIoctlGetTpcMasks(ServiceCtx Context) private long NvGpuIoctlGetTpcMasks(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -388,7 +409,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context) private long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -398,14 +419,14 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIoctlChannelSetUserData(ServiceCtx Context) private long NvMapIoctlChannelSetUserData(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
return 0; return 0;
} }
private static long NvMapIoctlChannelSetNvMap(ServiceCtx Context) private long NvMapIoctlChannelSetNvMap(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -414,7 +435,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context) private long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -453,7 +474,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context) private long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -465,7 +486,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIoctlChannelZcullBind(ServiceCtx Context) private long NvMapIoctlChannelZcullBind(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -478,7 +499,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context) private long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -492,7 +513,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIoctlChannelSetPriority(ServiceCtx Context) private long NvMapIoctlChannelSetPriority(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -501,7 +522,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context) private long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -521,47 +542,46 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIocCreate(ServiceCtx Context) private long NvMapIocCreate(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
int Size = Context.Memory.ReadInt32(Position); int Size = Context.Memory.ReadInt32(Position);
int Id = Context.Ns.Os.NvMapIds.GenerateId(); NvMap Map = new NvMap() { Size = Size };
int Handle = Context.Ns.Os.Handles.GenerateId(new HNvMap(Id, Size)); Map.Handle = NvMaps.Add(Map);
Context.Memory.WriteInt32(Position + 4, Handle); Map.Id = NvMapsById.Add(Map);
Logging.Info($"NvMap {Id} created with size {Size:x8}!"); Context.Memory.WriteInt32(Position + 4, Map.Handle);
Logging.Info($"NvMap {Map.Id} created with size {Size:x8}!");
return 0; return 0;
} }
private static long NvMapIocFromId(ServiceCtx Context) private long NvMapIocFromId(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
int Id = Context.Memory.ReadInt32(Position); int Id = Context.Memory.ReadInt32(Position);
int Handle = -1; NvMap Map = NvMapsById.GetData<NvMap>(Id);
foreach (KeyValuePair<int, object> KV in Context.Ns.Os.Handles) if (Map == null)
{ {
if (KV.Value is HNvMap NvMap && NvMap.Id == Id) Logging.Warn($"Trying to use invalid NvMap Id {Id}!");
{
Handle = KV.Key; return -1; //TODO: Corrent error code.
break;
}
} }
Context.Memory.WriteInt32(Position + 4, Handle); Context.Memory.WriteInt32(Position + 4, Map.Handle);
return 0; return 0;
} }
private static long NvMapIocAlloc(ServiceCtx Context) private long NvMapIocAlloc(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -574,38 +594,49 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
byte Kind = (byte)Reader.ReadInt64(); byte Kind = (byte)Reader.ReadInt64();
long Addr = Reader.ReadInt64(); long Addr = Reader.ReadInt64();
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle); NvMap Map = NvMaps.GetData<NvMap>(Handle);
if (NvMap != null) if (Map == null)
{ {
NvMap.Address = Addr; Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
NvMap.Align = Align;
NvMap.Kind = Kind; return -1; //TODO: Corrent error code.
} }
Map.Address = Addr;
Map.Align = Align;
Map.Kind = Kind;
return 0; return 0;
} }
private static long NvMapIocFree(ServiceCtx Context) private long NvMapIocFree(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position); MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position + 8); MemWriter Writer = new MemWriter(Context.Memory, Position + 8);
int Handle = Reader.ReadInt32(); int Handle = Reader.ReadInt32();
int Padding = Reader.ReadInt32(); int Padding = Reader.ReadInt32();
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle); NvMap Map = NvMaps.GetData<NvMap>(Handle);
if (Map == null)
{
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
return -1; //TODO: Corrent error code.
}
Writer.WriteInt64(0); Writer.WriteInt64(0);
Writer.WriteInt32(NvMap.Size); Writer.WriteInt32(Map.Size);
Writer.WriteInt32(0); Writer.WriteInt32(0);
return 0; return 0;
} }
private static long NvMapIocParam(ServiceCtx Context) private long NvMapIocParam(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -614,16 +645,23 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
int Handle = Reader.ReadInt32(); int Handle = Reader.ReadInt32();
int Param = Reader.ReadInt32(); int Param = Reader.ReadInt32();
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle); NvMap Map = NvMaps.GetData<NvMap>(Handle);
if (Map == null)
{
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
return -1; //TODO: Corrent error code.
}
int Response = 0; int Response = 0;
switch (Param) switch (Param)
{ {
case 1: Response = NvMap.Size; break; case 1: Response = Map.Size; break;
case 2: Response = NvMap.Align; break; case 2: Response = Map.Align; break;
case 4: Response = 0x40000000; break; case 4: Response = 0x40000000; break;
case 5: Response = NvMap.Kind; break; case 5: Response = Map.Kind; break;
} }
Context.Memory.WriteInt32(Position + 8, Response); Context.Memory.WriteInt32(Position + 8, Response);
@ -631,17 +669,29 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIocGetId(ServiceCtx Context) private long NvMapIocGetId(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
int Handle = Context.Memory.ReadInt32(Position + 4); int Handle = Context.Memory.ReadInt32(Position + 4);
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle); NvMap Map = NvMaps.GetData<NvMap>(Handle);
Context.Memory.WriteInt32(Position, NvMap.Id); if (Map == null)
{
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
return -1; //TODO: Corrent error code.
}
Context.Memory.WriteInt32(Position, Map.Id);
return 0; return 0;
} }
public NvMap GetNvMap(int Handle)
{
return NvMaps.GetData<NvMap>(Handle);
}
} }
} }

View file

@ -9,13 +9,13 @@ namespace Ryujinx.Core.OsHle.IpcServices
{ {
if (Context.Session is HDomain Dom) if (Context.Session is HDomain Dom)
{ {
Context.Response.ResponseObjIds.Add(Dom.GenerateObjectId(Obj)); Context.Response.ResponseObjIds.Add(Dom.Add(Obj));
} }
else else
{ {
HSessionObj HndData = new HSessionObj(Context.Session, Obj); HSessionObj HndData = new HSessionObj(Context.Session, Obj);
int VHandle = Context.Ns.Os.Handles.GenerateId(HndData); int VHandle = Context.Process.HandleTable.OpenHandle(HndData);
Context.Response.HandleDesc = IpcHandleDesc.MakeMove(VHandle); Context.Response.HandleDesc = IpcHandleDesc.MakeMove(VHandle);
} }

View file

@ -20,30 +20,32 @@ namespace Ryujinx.Core.OsHle.IpcServices.Pl
}; };
} }
public static long GetLoadState(ServiceCtx Context) public long GetLoadState(ServiceCtx Context)
{ {
Context.ResponseData.Write(1); //Loaded Context.ResponseData.Write(1); //Loaded
return 0; return 0;
} }
public static long GetFontSize(ServiceCtx Context) public long GetFontSize(ServiceCtx Context)
{ {
Context.ResponseData.Write(Horizon.FontSize); Context.ResponseData.Write(Horizon.FontSize);
return 0; return 0;
} }
public static long GetSharedMemoryAddressOffset(ServiceCtx Context) public long GetSharedMemoryAddressOffset(ServiceCtx Context)
{ {
Context.ResponseData.Write(0); Context.ResponseData.Write(0);
return 0; return 0;
} }
public static long GetSharedMemoryNativeHandle(ServiceCtx Context) public long GetSharedMemoryNativeHandle(ServiceCtx Context)
{ {
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.FontHandle); int Handle = Context.Process.HandleTable.OpenHandle(Context.Ns.Os.FontSharedMem);
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
return 0; return 0;
} }

View file

@ -1,64 +0,0 @@
using Ryujinx.Core.OsHle.IpcServices.Acc;
using Ryujinx.Core.OsHle.IpcServices.Am;
using Ryujinx.Core.OsHle.IpcServices.Apm;
using Ryujinx.Core.OsHle.IpcServices.Aud;
using Ryujinx.Core.OsHle.IpcServices.Bsd;
using Ryujinx.Core.OsHle.IpcServices.Friend;
using Ryujinx.Core.OsHle.IpcServices.FspSrv;
using Ryujinx.Core.OsHle.IpcServices.Hid;
using Ryujinx.Core.OsHle.IpcServices.Lm;
using Ryujinx.Core.OsHle.IpcServices.Nifm;
using Ryujinx.Core.OsHle.IpcServices.Ns;
using Ryujinx.Core.OsHle.IpcServices.NvServices;
using Ryujinx.Core.OsHle.IpcServices.Pctl;
using Ryujinx.Core.OsHle.IpcServices.Pl;
using Ryujinx.Core.OsHle.IpcServices.Set;
using Ryujinx.Core.OsHle.IpcServices.Sfdnsres;
using Ryujinx.Core.OsHle.IpcServices.Sm;
using Ryujinx.Core.OsHle.IpcServices.Ssl;
using Ryujinx.Core.OsHle.IpcServices.Time;
using Ryujinx.Core.OsHle.IpcServices.Vi;
using System;
namespace Ryujinx.Core.OsHle.IpcServices
{
static class ServiceFactory
{
public static IIpcService MakeService(string Name)
{
switch (Name)
{
case "acc:u0": return new ServiceAcc();
case "aoc:u": return new ServiceNs();
case "apm": return new ServiceApm();
case "apm:p": return new ServiceApm();
case "appletOE": return new ServiceAppletOE();
case "audout:u": return new ServiceAudOut();
case "audren:u": return new ServiceAudRen();
case "bsd:s": return new ServiceBsd();
case "bsd:u": return new ServiceBsd();
case "friend:a": return new ServiceFriend();
case "fsp-srv": return new ServiceFspSrv();
case "hid": return new ServiceHid();
case "lm": return new ServiceLm();
case "nifm:u": return new ServiceNifm();
case "nvdrv": return new ServiceNvDrv();
case "nvdrv:a": return new ServiceNvDrv();
case "pctl:a": return new ServicePctl();
case "pl:u": return new ServicePl();
case "set": return new ServiceSet();
case "set:sys": return new ServiceSetSys();
case "sfdnsres": return new ServiceSfdnsres();
case "sm:": return new ServiceSm();
case "ssl": return new ServiceSsl();
case "time:s": return new ServiceTime();
case "time:u": return new ServiceTime();
case "vi:m": return new ServiceVi();
case "vi:s": return new ServiceVi();
case "vi:u": return new ServiceVi();
}
throw new NotImplementedException(Name);
}
}
}

View file

@ -55,9 +55,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Sm
return 0; return 0;
} }
HSession Session = new HSession(ServiceFactory.MakeService(Name)); HSession Session = new HSession(Context.Process.Services.GetService(Name));
int Handle = Context.Ns.Os.Handles.GenerateId(Session); int Handle = Context.Process.HandleTable.OpenHandle(Session);
Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Core.OsHle namespace Ryujinx.Core.OsHle.IpcServices.Vi
{ {
class Display class Display
{ {

View file

@ -15,6 +15,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private IdDictionary Displays;
public IApplicationDisplayService() public IApplicationDisplayService()
{ {
m_Commands = new Dictionary<int, ServiceProcessRequest>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
@ -28,14 +30,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
{ 2020, OpenLayer }, { 2020, OpenLayer },
{ 2021, CloseLayer }, { 2021, CloseLayer },
{ 2030, CreateStrayLayer }, { 2030, CreateStrayLayer },
{ 2031, DestroyStrayLayer },
{ 2101, SetLayerScalingMode }, { 2101, SetLayerScalingMode },
{ 5202, GetDisplayVSyncEvent } { 5202, GetDisplayVSyncEvent }
}; };
Displays = new IdDictionary();
} }
public long GetRelayService(ServiceCtx Context) public long GetRelayService(ServiceCtx Context)
{ {
MakeObject(Context, new IHOSBinderDriver()); MakeObject(Context, new IHOSBinderDriver(Context.Ns.Gpu.Renderer));
return 0; return 0;
} }
@ -56,7 +61,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
public long GetIndirectDisplayTransactionService(ServiceCtx Context) public long GetIndirectDisplayTransactionService(ServiceCtx Context)
{ {
MakeObject(Context, new IHOSBinderDriver()); MakeObject(Context, new IHOSBinderDriver(Context.Ns.Gpu.Renderer));
return 0; return 0;
} }
@ -65,7 +70,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
{ {
string Name = GetDisplayName(Context); string Name = GetDisplayName(Context);
long DisplayId = Context.Ns.Os.Displays.GenerateId(new Display(Name)); long DisplayId = Displays.Add(new Display(Name));
Context.ResponseData.Write(DisplayId); Context.ResponseData.Write(DisplayId);
@ -76,7 +81,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
{ {
int DisplayId = Context.RequestData.ReadInt32(); int DisplayId = Context.RequestData.ReadInt32();
Context.Ns.Os.Displays.Delete(DisplayId); Displays.Delete(DisplayId);
return 0; return 0;
} }
@ -99,6 +104,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
public long CloseLayer(ServiceCtx Context) public long CloseLayer(ServiceCtx Context)
{ {
long LayerId = Context.RequestData.ReadInt64();
return 0; return 0;
} }
@ -109,7 +116,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
long ParcelPtr = Context.Request.ReceiveBuff[0].Position; long ParcelPtr = Context.Request.ReceiveBuff[0].Position;
Display Disp = Context.Ns.Os.Displays.GetData<Display>((int)DisplayId); Display Disp = Displays.GetData<Display>((int)DisplayId);
byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr); byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr);
@ -121,6 +128,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
return 0; return 0;
} }
public long DestroyStrayLayer(ServiceCtx Context)
{
return 0;
}
public long SetLayerScalingMode(ServiceCtx Context) public long SetLayerScalingMode(ServiceCtx Context)
{ {
int ScalingMode = Context.RequestData.ReadInt32(); int ScalingMode = Context.RequestData.ReadInt32();
@ -133,7 +145,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
{ {
string Name = GetDisplayName(Context); string Name = GetDisplayName(Context);
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent()); int Handle = Context.Process.HandleTable.OpenHandle(new HEvent());
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);

View file

@ -1,6 +1,7 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc; using Ryujinx.Core.OsHle.Ipc;
using Ryujinx.Core.OsHle.IpcServices.Android; using Ryujinx.Core.OsHle.IpcServices.Android;
using Ryujinx.Graphics.Gal;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -14,7 +15,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
private NvFlinger Flinger; private NvFlinger Flinger;
public IHOSBinderDriver() public IHOSBinderDriver(IGalRenderer Renderer)
{ {
m_Commands = new Dictionary<int, ServiceProcessRequest>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
{ {
@ -23,7 +24,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
{ 2, GetNativeHandle } { 2, GetNativeHandle }
}; };
Flinger = new NvFlinger(); Flinger = new NvFlinger(Renderer);
} }
public long TransactParcel(ServiceCtx Context) public long TransactParcel(ServiceCtx Context)

View file

@ -1,5 +1,6 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.IpcServices.NvServices;
using Ryujinx.Graphics.Gal;
using System; using System;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
@ -54,18 +55,27 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
public GbpBuffer Data; public GbpBuffer Data;
} }
private IGalRenderer Renderer;
private BufferEntry[] BufferQueue; private BufferEntry[] BufferQueue;
private ManualResetEvent WaitBufferFree; private ManualResetEvent WaitBufferFree;
private object RenderQueueLock;
private int RenderQueueCount;
private bool NvFlingerDisposed;
private bool KeepRunning; private bool KeepRunning;
public NvFlinger() public NvFlinger(IGalRenderer Renderer)
{ {
Commands = new Dictionary<(string, int), ServiceProcessParcel>() Commands = new Dictionary<(string, int), ServiceProcessParcel>()
{ {
{ ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer }, { ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer },
{ ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer }, { ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer },
{ ("android.gui.IGraphicBufferProducer", 0x4), GbpDetachBuffer },
{ ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer }, { ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer },
{ ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer }, { ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer },
{ ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery }, { ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery },
@ -74,10 +84,14 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
{ ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer } { ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer }
}; };
this.Renderer = Renderer;
BufferQueue = new BufferEntry[0x40]; BufferQueue = new BufferEntry[0x40];
WaitBufferFree = new ManualResetEvent(false); WaitBufferFree = new ManualResetEvent(false);
RenderQueueLock = new object();
KeepRunning = true; KeepRunning = true;
} }
@ -193,6 +207,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
return MakeReplyParcel(Context, 1280, 720, 0, 0, 0); return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
} }
private long GbpDetachBuffer(ServiceCtx Context, BinaryReader ParcelReader)
{
return MakeReplyParcel(Context, 0);
}
private long GbpCancelBuffer(ServiceCtx Context, BinaryReader ParcelReader) private long GbpCancelBuffer(ServiceCtx Context, BinaryReader ParcelReader)
{ {
//TODO: Errors. //TODO: Errors.
@ -266,7 +285,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
long FbSize = (uint)FbWidth * FbHeight * 4; long FbSize = (uint)FbWidth * FbHeight * 4;
HNvMap NvMap = GetNvMap(Context, Slot); NvMap NvMap = GetNvMap(Context, Slot);
if ((ulong)(NvMap.Address + FbSize) > AMemoryMgr.AddrSize) if ((ulong)(NvMap.Address + FbSize) > AMemoryMgr.AddrSize)
{ {
@ -330,7 +349,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
Rotate = -MathF.PI * 0.5f; Rotate = -MathF.PI * 0.5f;
} }
byte* Fb = (byte*)Context.Ns.Memory.Ram + NvMap.Address; lock (RenderQueueLock)
{
if (NvFlingerDisposed)
{
return;
}
Interlocked.Increment(ref RenderQueueCount);
}
byte* Fb = (byte*)Context.Memory.Ram + NvMap.Address;
Context.Ns.Gpu.Renderer.QueueAction(delegate() Context.Ns.Gpu.Renderer.QueueAction(delegate()
{ {
@ -346,6 +375,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
BufferQueue[Slot].State = BufferState.Free; BufferQueue[Slot].State = BufferState.Free;
Interlocked.Decrement(ref RenderQueueCount);
lock (WaitBufferFree) lock (WaitBufferFree)
{ {
WaitBufferFree.Set(); WaitBufferFree.Set();
@ -353,7 +384,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
}); });
} }
private HNvMap GetNvMap(ServiceCtx Context, int Slot) private NvMap GetNvMap(ServiceCtx Context, int Slot)
{ {
int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c); int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c);
@ -366,7 +397,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
NvMapHandle = BitConverter.ToInt32(RawValue, 0); NvMapHandle = BitConverter.ToInt32(RawValue, 0);
} }
return Context.Ns.Os.Handles.GetData<HNvMap>(NvMapHandle); ServiceNvDrv NvDrv = (ServiceNvDrv)Context.Process.Services.GetService("nvdrv");
return NvDrv.GetNvMap(NvMapHandle);
} }
private int GetFreeSlotBlocking(int Width, int Height) private int GetFreeSlotBlocking(int Width, int Height)
@ -432,10 +465,24 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
Dispose(true); Dispose(true);
} }
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool Disposing)
{ {
if (disposing) if (Disposing && !NvFlingerDisposed)
{ {
lock (RenderQueueLock)
{
NvFlingerDisposed = true;
}
//Ensure that all pending actions was sent before
//we can safely assume that the class was disposed.
while (RenderQueueCount > 0)
{
Thread.Yield();
}
Renderer.ResetFrameBuffer();
lock (WaitBufferFree) lock (WaitBufferFree)
{ {
KeepRunning = false; KeepRunning = false;

View file

@ -1,12 +1,13 @@
using ChocolArm64.Events; using ChocolArm64.Events;
using ChocolArm64.Memory; using ChocolArm64.Memory;
using ChocolArm64.State; using ChocolArm64.State;
using Ryujinx.Core.OsHle.Handles;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Svc namespace Ryujinx.Core.OsHle.Svc
{ {
partial class SvcHandler partial class SvcHandler : IDisposable
{ {
private delegate void SvcFunc(AThreadState ThreadState); private delegate void SvcFunc(AThreadState ThreadState);
@ -16,10 +17,12 @@ namespace Ryujinx.Core.OsHle.Svc
private Process Process; private Process Process;
private AMemory Memory; private AMemory Memory;
private static Random Rng; private HashSet<(HSharedMem, long)> MappedSharedMems;
private ulong CurrentHeapSize; private ulong CurrentHeapSize;
private static Random Rng;
public SvcHandler(Switch Ns, Process Process) public SvcHandler(Switch Ns, Process Process)
{ {
SvcFuncs = new Dictionary<int, SvcFunc>() SvcFuncs = new Dictionary<int, SvcFunc>()
@ -32,6 +35,7 @@ namespace Ryujinx.Core.OsHle.Svc
{ 0x07, SvcExitProcess }, { 0x07, SvcExitProcess },
{ 0x08, SvcCreateThread }, { 0x08, SvcCreateThread },
{ 0x09, SvcStartThread }, { 0x09, SvcStartThread },
{ 0x0a, SvcExitThread },
{ 0x0b, SvcSleepThread }, { 0x0b, SvcSleepThread },
{ 0x0c, SvcGetThreadPriority }, { 0x0c, SvcGetThreadPriority },
{ 0x0d, SvcSetThreadPriority }, { 0x0d, SvcSetThreadPriority },
@ -60,6 +64,8 @@ namespace Ryujinx.Core.OsHle.Svc
this.Ns = Ns; this.Ns = Ns;
this.Process = Process; this.Process = Process;
this.Memory = Process.Memory; this.Memory = Process.Memory;
MappedSharedMems = new HashSet<(HSharedMem, long)>();
} }
static SvcHandler() static SvcHandler()
@ -84,5 +90,26 @@ namespace Ryujinx.Core.OsHle.Svc
throw new NotImplementedException(e.Id.ToString("x4")); throw new NotImplementedException(e.Id.ToString("x4"));
} }
} }
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{
lock (MappedSharedMems)
{
foreach ((HSharedMem SharedMem, long Position) in MappedSharedMems)
{
SharedMem.RemoveVirtualPosition(Memory, Position);
}
MappedSharedMems.Clear();
}
}
}
} }
} }

View file

@ -165,7 +165,7 @@ namespace Ryujinx.Core.OsHle.Svc
return; return;
} }
HSharedMem SharedMem = Ns.Os.Handles.GetData<HSharedMem>(Handle); HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle);
if (SharedMem != null) if (SharedMem != null)
{ {
@ -175,7 +175,12 @@ namespace Ryujinx.Core.OsHle.Svc
Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
SharedMem.AddVirtualPosition(Src); lock (MappedSharedMems)
{
MappedSharedMems.Add((SharedMem, Src));
}
SharedMem.AddVirtualPosition(Memory, Src);
ThreadState.X0 = 0; ThreadState.X0 = 0;
} }
@ -198,12 +203,19 @@ namespace Ryujinx.Core.OsHle.Svc
return; return;
} }
HSharedMem HndData = Ns.Os.Handles.GetData<HSharedMem>(Handle); HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle);
if (HndData != null) if (SharedMem != null)
{ {
Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory); Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory);
SharedMem.RemoveVirtualPosition(Memory, Src);
lock (MappedSharedMems)
{
MappedSharedMems.Remove((SharedMem, Src));
}
ThreadState.X0 = 0; ThreadState.X0 = 0;
} }
@ -229,12 +241,12 @@ namespace Ryujinx.Core.OsHle.Svc
Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
HTransferMem HndData = new HTransferMem(Memory, MapInfo.Perm, Src, Size); HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size);
int Handle = Ns.Os.Handles.GenerateId(HndData); ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem);
ThreadState.X1 = (ulong)Handle;
ThreadState.X0 = 0; ThreadState.X0 = 0;
ThreadState.X1 = Handle;
} }
private static bool IsValidPosition(long Position) private static bool IsValidPosition(long Position)

View file

@ -3,7 +3,6 @@ using ChocolArm64.State;
using Ryujinx.Core.OsHle.Exceptions; using Ryujinx.Core.OsHle.Exceptions;
using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc; using Ryujinx.Core.OsHle.Ipc;
using Ryujinx.Core.OsHle.IpcServices;
using System; using System;
using System.Threading; using System.Threading;
@ -35,7 +34,7 @@ namespace Ryujinx.Core.OsHle.Svc
{ {
int Handle = (int)ThreadState.X0; int Handle = (int)ThreadState.X0;
Ns.Os.CloseHandle(Handle); Process.HandleTable.CloseHandle(Handle);
ThreadState.X0 = 0; ThreadState.X0 = 0;
} }
@ -80,10 +79,12 @@ namespace Ryujinx.Core.OsHle.Svc
//TODO: Validate that app has perms to access the service, and that the service //TODO: Validate that app has perms to access the service, and that the service
//actually exists, return error codes otherwise. //actually exists, return error codes otherwise.
HSession Session = new HSession(ServiceFactory.MakeService(Name)); HSession Session = new HSession(Process.Services.GetService(Name));
ThreadState.X1 = (ulong)Ns.Os.Handles.GenerateId(Session); ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session);
ThreadState.X0 = 0; ThreadState.X0 = 0;
ThreadState.X1 = Handle;
} }
private void SvcSendSyncRequest(AThreadState ThreadState) private void SvcSendSyncRequest(AThreadState ThreadState)
@ -119,13 +120,21 @@ namespace Ryujinx.Core.OsHle.Svc
byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size); byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
HSession Session = Ns.Os.Handles.GetData<HSession>(Handle); HSession Session = Process.HandleTable.GetData<HSession>(Handle);
IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr, Session is HDomain); IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr, Session is HDomain);
if (Session != null) if (Session != null)
{ {
IpcHandler.IpcCall(Ns, Memory, Session, Cmd, ThreadState.ThreadId, CmdPtr, Handle); IpcHandler.IpcCall(
Ns,
Process,
Memory,
Session,
Cmd,
ThreadState.ThreadId,
CmdPtr,
Handle);
byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size); byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);

View file

@ -39,7 +39,7 @@ namespace Ryujinx.Core.OsHle.Svc
{ {
int Handle = (int)ThreadState.X0; int Handle = (int)ThreadState.X0;
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle); HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
if (Thread != null) if (Thread != null)
{ {
@ -51,6 +51,13 @@ namespace Ryujinx.Core.OsHle.Svc
//TODO: Error codes. //TODO: Error codes.
} }
private void SvcExitThread(AThreadState ThreadState)
{
HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
CurrThread.Thread.StopExecution();
}
private void SvcSleepThread(AThreadState ThreadState) private void SvcSleepThread(AThreadState ThreadState)
{ {
ulong NanoSecs = ThreadState.X0; ulong NanoSecs = ThreadState.X0;
@ -71,7 +78,7 @@ namespace Ryujinx.Core.OsHle.Svc
{ {
int Handle = (int)ThreadState.X1; int Handle = (int)ThreadState.X1;
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle); HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
if (Thread != null) if (Thread != null)
{ {
@ -87,7 +94,7 @@ namespace Ryujinx.Core.OsHle.Svc
int Handle = (int)ThreadState.X1; int Handle = (int)ThreadState.X1;
int Prio = (int)ThreadState.X0; int Prio = (int)ThreadState.X0;
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle); HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
if (Thread != null) if (Thread != null)
{ {
@ -110,7 +117,7 @@ namespace Ryujinx.Core.OsHle.Svc
{ {
int Handle = (int)ThreadState.X0; int Handle = (int)ThreadState.X0;
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle); HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
if (Thread != null) if (Thread != null)
{ {

View file

@ -13,7 +13,7 @@ namespace Ryujinx.Core.OsHle.Svc
long MutexAddress = (long)ThreadState.X1; long MutexAddress = (long)ThreadState.X1;
int RequestingThreadHandle = (int)ThreadState.X2; int RequestingThreadHandle = (int)ThreadState.X2;
HThread RequestingThread = Ns.Os.Handles.GetData<HThread>(RequestingThreadHandle); HThread RequestingThread = Process.HandleTable.GetData<HThread>(RequestingThreadHandle);
Mutex M = new Mutex(Process, MutexAddress, OwnerThreadHandle); Mutex M = new Mutex(Process, MutexAddress, OwnerThreadHandle);
@ -43,7 +43,7 @@ namespace Ryujinx.Core.OsHle.Svc
int ThreadHandle = (int)ThreadState.X2; int ThreadHandle = (int)ThreadState.X2;
long Timeout = (long)ThreadState.X3; long Timeout = (long)ThreadState.X3;
HThread Thread = Ns.Os.Handles.GetData<HThread>(ThreadHandle); HThread Thread = Process.HandleTable.GetData<HThread>(ThreadHandle);
Mutex M = new Mutex(Process, MutexAddress, ThreadHandle); Mutex M = new Mutex(Process, MutexAddress, ThreadHandle);

View file

@ -1,53 +0,0 @@
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Utilities
{
class IdPool
{
private HashSet<int> Ids;
private int CurrId;
private int MinId;
private int MaxId;
public IdPool(int Min, int Max)
{
Ids = new HashSet<int>();
CurrId = Min;
MinId = Min;
MaxId = Max;
}
public IdPool() : this(1, int.MaxValue) { }
public int GenerateId()
{
lock (Ids)
{
for (int Cnt = MinId; Cnt < MaxId; Cnt++)
{
if (Ids.Add(CurrId))
{
return CurrId;
}
if (CurrId++ == MaxId)
{
CurrId = MinId;
}
}
return -1;
}
}
public bool DeleteId(int Id)
{
lock (Ids)
{
return Ids.Remove(Id);
}
}
}
}

View file

@ -1,4 +1,3 @@
using ChocolArm64.Memory;
using Ryujinx.Core.Input; using Ryujinx.Core.Input;
using Ryujinx.Core.OsHle; using Ryujinx.Core.OsHle;
using Ryujinx.Core.Settings; using Ryujinx.Core.Settings;
@ -10,8 +9,6 @@ namespace Ryujinx.Core
{ {
public class Switch : IDisposable public class Switch : IDisposable
{ {
internal AMemory Memory { get; private set; }
internal NsGpu Gpu { get; private set; } internal NsGpu Gpu { get; private set; }
internal Horizon Os { get; private set; } internal Horizon Os { get; private set; }
internal VirtualFs VFs { get; private set; } internal VirtualFs VFs { get; private set; }
@ -24,13 +21,11 @@ namespace Ryujinx.Core
public Switch(IGalRenderer Renderer) public Switch(IGalRenderer Renderer)
{ {
Memory = new AMemory();
Gpu = new NsGpu(Renderer); Gpu = new NsGpu(Renderer);
VFs = new VirtualFs(); VFs = new VirtualFs();
Hid = new Hid(Memory); Hid = new Hid();
Statistics = new PerformanceStatistics(); Statistics = new PerformanceStatistics();
@ -42,11 +37,6 @@ namespace Ryujinx.Core
Settings = new SetSys(); Settings = new SetSys();
} }
public void FinalizeAllProcesses()
{
Os.FinalizeAllProcesses();
}
public void LoadCart(string ExeFsDir, string RomFsFile = null) public void LoadCart(string ExeFsDir, string RomFsFile = null)
{ {
Os.LoadCart(ExeFsDir, RomFsFile); Os.LoadCart(ExeFsDir, RomFsFile);
@ -67,12 +57,11 @@ namespace Ryujinx.Core
Dispose(true); Dispose(true);
} }
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool Disposing)
{ {
if (disposing) if (Disposing)
{ {
Memory.Dispose(); Os.Dispose();
VFs.Dispose(); VFs.Dispose();
} }
} }

View file

@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Gal
void RunActions(); void RunActions();
void InitializeFrameBuffer(); void InitializeFrameBuffer();
void ResetFrameBuffer();
void Render(); void Render();
void SetWindowSize(int Width, int Height); void SetWindowSize(int Width, int Height);
void SetFrameBuffer( void SetFrameBuffer(

View file

@ -24,6 +24,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private byte* FbPtr; private byte* FbPtr;
private object FbPtrLock;
public FrameBuffer(int Width, int Height) public FrameBuffer(int Width, int Height)
{ {
if (Width < 0) if (Width < 0)
@ -36,6 +38,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
throw new ArgumentOutOfRangeException(nameof(Height)); throw new ArgumentOutOfRangeException(nameof(Height));
} }
FbPtrLock = new object();
TexWidth = Width; TexWidth = Width;
TexHeight = Height; TexHeight = Height;
@ -152,7 +156,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
throw new ArgumentOutOfRangeException(nameof(Height)); throw new ArgumentOutOfRangeException(nameof(Height));
} }
FbPtr = Fb; lock (FbPtrLock)
{
FbPtr = Fb;
}
if (Width != TexWidth || if (Width != TexWidth ||
Height != TexHeight) Height != TexHeight)
@ -178,17 +185,28 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.Uniform2(OffsetUniformLocation, Offs); GL.Uniform2(OffsetUniformLocation, Offs);
} }
public void Reset()
{
lock (FbPtrLock)
{
FbPtr = null;
}
}
public void Render() public void Render()
{ {
if (FbPtr == null) lock (FbPtrLock)
{ {
return; if (FbPtr == null)
} {
return;
}
for (int Y = 0; Y < TexHeight; Y++) for (int Y = 0; Y < TexHeight; Y++)
for (int X = 0; X < TexWidth; X++) for (int X = 0; X < TexWidth; X++)
{ {
Pixels[X + Y * TexWidth] = *((int*)(FbPtr + GetSwizzleOffset(X, Y))); Pixels[X + Y * TexWidth] = *((int*)(FbPtr + GetSwizzleOffset(X, Y)));
}
} }
GL.BindTexture(TextureTarget.Texture2D, TexHandle); GL.BindTexture(TextureTarget.Texture2D, TexHandle);

View file

@ -43,6 +43,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
FbRenderer = new FrameBuffer(1280, 720); FbRenderer = new FrameBuffer(1280, 720);
} }
public void ResetFrameBuffer()
{
FbRenderer.Reset();
}
public void QueueAction(Action ActionMthd) public void QueueAction(Action ActionMthd)
{ {
ActionsQueue.Enqueue(ActionMthd); ActionsQueue.Enqueue(ActionMthd);

View file

@ -28,7 +28,7 @@ namespace Ryujinx.Tests.Cpu
ATranslator Translator = new ATranslator(); ATranslator Translator = new ATranslator();
Memory = new AMemory(); Memory = new AMemory();
Memory.Manager.Map(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute); Memory.Manager.Map(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute);
Thread = new AThread(Translator, Memory, ThreadPriority.Normal, EntryPoint); Thread = new AThread(Translator, Memory, EntryPoint);
} }
[TearDown] [TearDown]