Rename Ryujinx.Core to Ryujinx.HLE and add a separate project for a future LLE implementation
This commit is contained in:
parent
518fe799da
commit
76f3b1b3a4
248 changed files with 2266 additions and 2244 deletions
17
Ryujinx.HLE/OsHle/Kernel/KernelErr.cs
Normal file
17
Ryujinx.HLE/OsHle/Kernel/KernelErr.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
namespace Ryujinx.HLE.OsHle.Kernel
|
||||
{
|
||||
static class KernelErr
|
||||
{
|
||||
public const int InvalidAlignment = 102;
|
||||
public const int InvalidAddress = 106;
|
||||
public const int InvalidMemRange = 110;
|
||||
public const int InvalidPriority = 112;
|
||||
public const int InvalidCoreId = 113;
|
||||
public const int InvalidHandle = 114;
|
||||
public const int InvalidCoreMask = 116;
|
||||
public const int Timeout = 117;
|
||||
public const int Canceled = 118;
|
||||
public const int CountOutOfRange = 119;
|
||||
public const int InvalidInfo = 120;
|
||||
}
|
||||
}
|
19
Ryujinx.HLE/OsHle/Kernel/NsTimeConverter.cs
Normal file
19
Ryujinx.HLE/OsHle/Kernel/NsTimeConverter.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
namespace Ryujinx.HLE.OsHle.Kernel
|
||||
{
|
||||
static class NsTimeConverter
|
||||
{
|
||||
public static int GetTimeMs(ulong Ns)
|
||||
{
|
||||
ulong Ms = Ns / 1_000_000;
|
||||
|
||||
if (Ms < int.MaxValue)
|
||||
{
|
||||
return (int)Ms;
|
||||
}
|
||||
else
|
||||
{
|
||||
return int.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
147
Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs
Normal file
147
Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs
Normal file
|
@ -0,0 +1,147 @@
|
|||
using ChocolArm64.Events;
|
||||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.State;
|
||||
using Ryujinx.HLE.Logging;
|
||||
using Ryujinx.HLE.OsHle.Handles;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.OsHle.Kernel
|
||||
{
|
||||
partial class SvcHandler : IDisposable
|
||||
{
|
||||
private delegate void SvcFunc(AThreadState ThreadState);
|
||||
|
||||
private Dictionary<int, SvcFunc> SvcFuncs;
|
||||
|
||||
private Switch Ns;
|
||||
private Process Process;
|
||||
private AMemory Memory;
|
||||
|
||||
private ConcurrentDictionary<KThread, AutoResetEvent> SyncWaits;
|
||||
|
||||
private HashSet<(HSharedMem, long)> MappedSharedMems;
|
||||
|
||||
private ulong CurrentHeapSize;
|
||||
|
||||
private const uint SelfThreadHandle = 0xffff8000;
|
||||
private const uint SelfProcessHandle = 0xffff8001;
|
||||
|
||||
private static Random Rng;
|
||||
|
||||
public SvcHandler(Switch Ns, Process Process)
|
||||
{
|
||||
SvcFuncs = new Dictionary<int, SvcFunc>()
|
||||
{
|
||||
{ 0x01, SvcSetHeapSize },
|
||||
{ 0x03, SvcSetMemoryAttribute },
|
||||
{ 0x04, SvcMapMemory },
|
||||
{ 0x05, SvcUnmapMemory },
|
||||
{ 0x06, SvcQueryMemory },
|
||||
{ 0x07, SvcExitProcess },
|
||||
{ 0x08, SvcCreateThread },
|
||||
{ 0x09, SvcStartThread },
|
||||
{ 0x0a, SvcExitThread },
|
||||
{ 0x0b, SvcSleepThread },
|
||||
{ 0x0c, SvcGetThreadPriority },
|
||||
{ 0x0d, SvcSetThreadPriority },
|
||||
{ 0x0e, SvcGetThreadCoreMask },
|
||||
{ 0x0f, SvcSetThreadCoreMask },
|
||||
{ 0x10, SvcGetCurrentProcessorNumber },
|
||||
{ 0x12, SvcClearEvent },
|
||||
{ 0x13, SvcMapSharedMemory },
|
||||
{ 0x14, SvcUnmapSharedMemory },
|
||||
{ 0x15, SvcCreateTransferMemory },
|
||||
{ 0x16, SvcCloseHandle },
|
||||
{ 0x17, SvcResetSignal },
|
||||
{ 0x18, SvcWaitSynchronization },
|
||||
{ 0x19, SvcCancelSynchronization },
|
||||
{ 0x1a, SvcArbitrateLock },
|
||||
{ 0x1b, SvcArbitrateUnlock },
|
||||
{ 0x1c, SvcWaitProcessWideKeyAtomic },
|
||||
{ 0x1d, SvcSignalProcessWideKey },
|
||||
{ 0x1e, SvcGetSystemTick },
|
||||
{ 0x1f, SvcConnectToNamedPort },
|
||||
{ 0x21, SvcSendSyncRequest },
|
||||
{ 0x22, SvcSendSyncRequestWithUserBuffer },
|
||||
{ 0x25, SvcGetThreadId },
|
||||
{ 0x26, SvcBreak },
|
||||
{ 0x27, SvcOutputDebugString },
|
||||
{ 0x29, SvcGetInfo },
|
||||
{ 0x2c, SvcMapPhysicalMemory },
|
||||
{ 0x2d, SvcUnmapPhysicalMemory },
|
||||
{ 0x32, SvcSetThreadActivity }
|
||||
};
|
||||
|
||||
this.Ns = Ns;
|
||||
this.Process = Process;
|
||||
this.Memory = Process.Memory;
|
||||
|
||||
SyncWaits = new ConcurrentDictionary<KThread, AutoResetEvent>();
|
||||
|
||||
MappedSharedMems = new HashSet<(HSharedMem, long)>();
|
||||
}
|
||||
|
||||
static SvcHandler()
|
||||
{
|
||||
Rng = new Random();
|
||||
}
|
||||
|
||||
public void SvcCall(object sender, AInstExceptionEventArgs e)
|
||||
{
|
||||
AThreadState ThreadState = (AThreadState)sender;
|
||||
|
||||
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
|
||||
{
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} called.");
|
||||
|
||||
Func(ThreadState);
|
||||
|
||||
Process.Scheduler.Reschedule(Process.GetThread(ThreadState.Tpidr));
|
||||
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} ended.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Process.PrintStackTrace(ThreadState);
|
||||
|
||||
throw new NotImplementedException(e.Id.ToString("x4"));
|
||||
}
|
||||
}
|
||||
|
||||
private KThread GetThread(long Tpidr, int Handle)
|
||||
{
|
||||
if ((uint)Handle == SelfThreadHandle)
|
||||
{
|
||||
return Process.GetThread(Tpidr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Process.HandleTable.GetData<KThread>(Handle);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
285
Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs
Normal file
285
Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs
Normal file
|
@ -0,0 +1,285 @@
|
|||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.State;
|
||||
using Ryujinx.HLE.Logging;
|
||||
using Ryujinx.HLE.OsHle.Handles;
|
||||
|
||||
using static Ryujinx.HLE.OsHle.ErrorCode;
|
||||
|
||||
namespace Ryujinx.HLE.OsHle.Kernel
|
||||
{
|
||||
partial class SvcHandler
|
||||
{
|
||||
private void SvcSetHeapSize(AThreadState ThreadState)
|
||||
{
|
||||
uint Size = (uint)ThreadState.X1;
|
||||
|
||||
long Position = MemoryRegions.HeapRegionAddress;
|
||||
|
||||
if (Size > CurrentHeapSize)
|
||||
{
|
||||
Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW);
|
||||
}
|
||||
else
|
||||
{
|
||||
Memory.Manager.Unmap(Position + Size, (long)CurrentHeapSize - Size);
|
||||
}
|
||||
|
||||
CurrentHeapSize = Size;
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
ThreadState.X1 = (ulong)Position;
|
||||
}
|
||||
|
||||
private void SvcSetMemoryAttribute(AThreadState ThreadState)
|
||||
{
|
||||
long Position = (long)ThreadState.X0;
|
||||
long Size = (long)ThreadState.X1;
|
||||
int State0 = (int)ThreadState.X2;
|
||||
int State1 = (int)ThreadState.X3;
|
||||
|
||||
if ((State0 == 0 && State1 == 0) ||
|
||||
(State0 == 8 && State1 == 0))
|
||||
{
|
||||
Memory.Manager.ClearAttrBit(Position, Size, 3);
|
||||
}
|
||||
else if (State0 == 8 && State1 == 8)
|
||||
{
|
||||
Memory.Manager.SetAttrBit(Position, Size, 3);
|
||||
}
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
private void SvcMapMemory(AThreadState ThreadState)
|
||||
{
|
||||
long Dst = (long)ThreadState.X0;
|
||||
long Src = (long)ThreadState.X1;
|
||||
long Size = (long)ThreadState.X2;
|
||||
|
||||
if (!IsValidPosition(Src))
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid src address {Src:x16}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsValidMapPosition(Dst))
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid dst address {Dst:x16}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src);
|
||||
|
||||
Memory.Manager.Map(Dst, Size, (int)MemoryType.MappedMemory, SrcInfo.Perm);
|
||||
|
||||
Memory.Manager.Reprotect(Src, Size, AMemoryPerm.None);
|
||||
|
||||
Memory.Manager.SetAttrBit(Src, Size, 0);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
private void SvcUnmapMemory(AThreadState ThreadState)
|
||||
{
|
||||
long Dst = (long)ThreadState.X0;
|
||||
long Src = (long)ThreadState.X1;
|
||||
long Size = (long)ThreadState.X2;
|
||||
|
||||
if (!IsValidPosition(Src))
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid src address {Src:x16}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsValidMapPosition(Dst))
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid dst address {Dst:x16}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst);
|
||||
|
||||
Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory);
|
||||
|
||||
Memory.Manager.Reprotect(Src, Size, DstInfo.Perm);
|
||||
|
||||
Memory.Manager.ClearAttrBit(Src, Size, 0);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
private void SvcQueryMemory(AThreadState ThreadState)
|
||||
{
|
||||
long InfoPtr = (long)ThreadState.X0;
|
||||
long Position = (long)ThreadState.X2;
|
||||
|
||||
AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position);
|
||||
|
||||
if (MapInfo == null)
|
||||
{
|
||||
long AddrSpaceEnd = MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
|
||||
|
||||
long ReservedSize = (long)(ulong.MaxValue - (ulong)AddrSpaceEnd) + 1;
|
||||
|
||||
MapInfo = new AMemoryMapInfo(AddrSpaceEnd, ReservedSize, (int)MemoryType.Reserved, 0, AMemoryPerm.None);
|
||||
}
|
||||
|
||||
Memory.WriteInt64(InfoPtr + 0x00, MapInfo.Position);
|
||||
Memory.WriteInt64(InfoPtr + 0x08, MapInfo.Size);
|
||||
Memory.WriteInt32(InfoPtr + 0x10, MapInfo.Type);
|
||||
Memory.WriteInt32(InfoPtr + 0x14, MapInfo.Attr);
|
||||
Memory.WriteInt32(InfoPtr + 0x18, (int)MapInfo.Perm);
|
||||
Memory.WriteInt32(InfoPtr + 0x1c, 0);
|
||||
Memory.WriteInt32(InfoPtr + 0x20, 0);
|
||||
Memory.WriteInt32(InfoPtr + 0x24, 0);
|
||||
//TODO: X1.
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
ThreadState.X1 = 0;
|
||||
}
|
||||
|
||||
private void SvcMapSharedMemory(AThreadState ThreadState)
|
||||
{
|
||||
int Handle = (int)ThreadState.X0;
|
||||
long Src = (long)ThreadState.X1;
|
||||
long Size = (long)ThreadState.X2;
|
||||
int Perm = (int)ThreadState.X3;
|
||||
|
||||
if (!IsValidPosition(Src))
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle);
|
||||
|
||||
if (SharedMem != null)
|
||||
{
|
||||
Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, AMemoryPerm.Write);
|
||||
|
||||
AMemoryHelper.FillWithZeros(Memory, Src, (int)Size);
|
||||
|
||||
Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
|
||||
|
||||
lock (MappedSharedMems)
|
||||
{
|
||||
MappedSharedMems.Add((SharedMem, Src));
|
||||
}
|
||||
|
||||
SharedMem.AddVirtualPosition(Memory, Src);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
//TODO: Error codes.
|
||||
}
|
||||
|
||||
private void SvcUnmapSharedMemory(AThreadState ThreadState)
|
||||
{
|
||||
int Handle = (int)ThreadState.X0;
|
||||
long Src = (long)ThreadState.X1;
|
||||
long Size = (long)ThreadState.X2;
|
||||
|
||||
if (!IsValidPosition(Src))
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle);
|
||||
|
||||
if (SharedMem != null)
|
||||
{
|
||||
Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory);
|
||||
|
||||
SharedMem.RemoveVirtualPosition(Memory, Src);
|
||||
|
||||
lock (MappedSharedMems)
|
||||
{
|
||||
MappedSharedMems.Remove((SharedMem, Src));
|
||||
}
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
//TODO: Error codes.
|
||||
}
|
||||
|
||||
private void SvcCreateTransferMemory(AThreadState ThreadState)
|
||||
{
|
||||
long Src = (long)ThreadState.X1;
|
||||
long Size = (long)ThreadState.X2;
|
||||
int Perm = (int)ThreadState.X3;
|
||||
|
||||
if (!IsValidPosition(Src))
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Src);
|
||||
|
||||
Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
|
||||
|
||||
HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size);
|
||||
|
||||
ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
ThreadState.X1 = Handle;
|
||||
}
|
||||
|
||||
private void SvcMapPhysicalMemory(AThreadState ThreadState)
|
||||
{
|
||||
long Position = (long)ThreadState.X0;
|
||||
uint Size = (uint)ThreadState.X1;
|
||||
|
||||
Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
private void SvcUnmapPhysicalMemory(AThreadState ThreadState)
|
||||
{
|
||||
long Position = (long)ThreadState.X0;
|
||||
uint Size = (uint)ThreadState.X1;
|
||||
|
||||
Memory.Manager.Unmap(Position, Size);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
private static bool IsValidPosition(long Position)
|
||||
{
|
||||
return Position >= MemoryRegions.AddrSpaceStart &&
|
||||
Position < MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
|
||||
}
|
||||
|
||||
private static bool IsValidMapPosition(long Position)
|
||||
{
|
||||
return Position >= MemoryRegions.MapRegionAddress &&
|
||||
Position < MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize;
|
||||
}
|
||||
}
|
||||
}
|
369
Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs
Normal file
369
Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs
Normal file
|
@ -0,0 +1,369 @@
|
|||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.State;
|
||||
using Ryujinx.HLE.Logging;
|
||||
using Ryujinx.HLE.OsHle.Exceptions;
|
||||
using Ryujinx.HLE.OsHle.Handles;
|
||||
using Ryujinx.HLE.OsHle.Ipc;
|
||||
using Ryujinx.HLE.OsHle.Services;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
using static Ryujinx.HLE.OsHle.ErrorCode;
|
||||
|
||||
namespace Ryujinx.HLE.OsHle.Kernel
|
||||
{
|
||||
partial class SvcHandler
|
||||
{
|
||||
private const int AllowedCpuIdBitmask = 0b1111;
|
||||
|
||||
private const bool EnableProcessDebugging = false;
|
||||
|
||||
private const bool IsVirtualMemoryEnabled = true; //This is always true(?)
|
||||
|
||||
private void SvcExitProcess(AThreadState ThreadState)
|
||||
{
|
||||
Ns.Os.ExitProcess(ThreadState.ProcessId);
|
||||
}
|
||||
|
||||
private void SvcClearEvent(AThreadState ThreadState)
|
||||
{
|
||||
int Handle = (int)ThreadState.X0;
|
||||
|
||||
//TODO: Implement events.
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
private void SvcCloseHandle(AThreadState ThreadState)
|
||||
{
|
||||
int Handle = (int)ThreadState.X0;
|
||||
|
||||
object Obj = Process.HandleTable.CloseHandle(Handle);
|
||||
|
||||
if (Obj == null)
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid handle 0x{Handle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Obj is KSession Session)
|
||||
{
|
||||
Session.Dispose();
|
||||
}
|
||||
else if (Obj is HTransferMem TMem)
|
||||
{
|
||||
TMem.Memory.Manager.Reprotect(
|
||||
TMem.Position,
|
||||
TMem.Size,
|
||||
TMem.Perm);
|
||||
}
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
private void SvcResetSignal(AThreadState ThreadState)
|
||||
{
|
||||
int Handle = (int)ThreadState.X0;
|
||||
|
||||
KEvent Event = Process.HandleTable.GetData<KEvent>(Handle);
|
||||
|
||||
if (Event != null)
|
||||
{
|
||||
Event.WaitEvent.Reset();
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid event handle 0x{Handle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private void SvcWaitSynchronization(AThreadState ThreadState)
|
||||
{
|
||||
long HandlesPtr = (long)ThreadState.X1;
|
||||
int HandlesCount = (int)ThreadState.X2;
|
||||
ulong Timeout = ThreadState.X3;
|
||||
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc,
|
||||
"HandlesPtr = " + HandlesPtr .ToString("x16") + ", " +
|
||||
"HandlesCount = " + HandlesCount.ToString("x8") + ", " +
|
||||
"Timeout = " + Timeout .ToString("x16"));
|
||||
|
||||
if ((uint)HandlesCount > 0x40)
|
||||
{
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.CountOutOfRange);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
||||
|
||||
WaitHandle[] Handles = new WaitHandle[HandlesCount + 1];
|
||||
|
||||
for (int Index = 0; Index < HandlesCount; Index++)
|
||||
{
|
||||
int Handle = Memory.ReadInt32(HandlesPtr + Index * 4);
|
||||
|
||||
KSynchronizationObject SyncObj = Process.HandleTable.GetData<KSynchronizationObject>(Handle);
|
||||
|
||||
if (SyncObj == null)
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid handle 0x{Handle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Handles[Index] = SyncObj.WaitEvent;
|
||||
}
|
||||
|
||||
using (AutoResetEvent WaitEvent = new AutoResetEvent(false))
|
||||
{
|
||||
if (!SyncWaits.TryAdd(CurrThread, WaitEvent))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
Handles[HandlesCount] = WaitEvent;
|
||||
|
||||
Process.Scheduler.Suspend(CurrThread);
|
||||
|
||||
int HandleIndex;
|
||||
|
||||
ulong Result = 0;
|
||||
|
||||
if (Timeout != ulong.MaxValue)
|
||||
{
|
||||
HandleIndex = WaitHandle.WaitAny(Handles, NsTimeConverter.GetTimeMs(Timeout));
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleIndex = WaitHandle.WaitAny(Handles);
|
||||
}
|
||||
|
||||
if (HandleIndex == WaitHandle.WaitTimeout)
|
||||
{
|
||||
Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||
}
|
||||
else if (HandleIndex == HandlesCount)
|
||||
{
|
||||
Result = MakeError(ErrorModule.Kernel, KernelErr.Canceled);
|
||||
}
|
||||
|
||||
SyncWaits.TryRemove(CurrThread, out _);
|
||||
|
||||
Process.Scheduler.Resume(CurrThread);
|
||||
|
||||
ThreadState.X0 = Result;
|
||||
|
||||
if (Result == 0)
|
||||
{
|
||||
ThreadState.X1 = (ulong)HandleIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SvcCancelSynchronization(AThreadState ThreadState)
|
||||
{
|
||||
int ThreadHandle = (int)ThreadState.X0;
|
||||
|
||||
KThread Thread = GetThread(ThreadState.Tpidr, ThreadHandle);
|
||||
|
||||
if (Thread == null)
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (SyncWaits.TryRemove(Thread, out AutoResetEvent WaitEvent))
|
||||
{
|
||||
WaitEvent.Set();
|
||||
}
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
private void SvcGetSystemTick(AThreadState ThreadState)
|
||||
{
|
||||
ThreadState.X0 = ThreadState.CntpctEl0;
|
||||
}
|
||||
|
||||
private void SvcConnectToNamedPort(AThreadState ThreadState)
|
||||
{
|
||||
long StackPtr = (long)ThreadState.X0;
|
||||
long NamePtr = (long)ThreadState.X1;
|
||||
|
||||
string Name = AMemoryHelper.ReadAsciiString(Memory, NamePtr, 8);
|
||||
|
||||
//TODO: Validate that app has perms to access the service, and that the service
|
||||
//actually exists, return error codes otherwise.
|
||||
KSession Session = new KSession(ServiceFactory.MakeService(Name), Name);
|
||||
|
||||
ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
ThreadState.X1 = Handle;
|
||||
}
|
||||
|
||||
private void SvcSendSyncRequest(AThreadState ThreadState)
|
||||
{
|
||||
SendSyncRequest(ThreadState, ThreadState.Tpidr, 0x100, (int)ThreadState.X0);
|
||||
}
|
||||
|
||||
private void SvcSendSyncRequestWithUserBuffer(AThreadState ThreadState)
|
||||
{
|
||||
SendSyncRequest(
|
||||
ThreadState,
|
||||
(long)ThreadState.X0,
|
||||
(long)ThreadState.X1,
|
||||
(int)ThreadState.X2);
|
||||
}
|
||||
|
||||
private void SendSyncRequest(AThreadState ThreadState, long CmdPtr, long Size, int Handle)
|
||||
{
|
||||
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
||||
|
||||
byte[] CmdData = Memory.ReadBytes(CmdPtr, Size);
|
||||
|
||||
KSession Session = Process.HandleTable.GetData<KSession>(Handle);
|
||||
|
||||
if (Session != null)
|
||||
{
|
||||
Process.Scheduler.Suspend(CurrThread);
|
||||
|
||||
IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr);
|
||||
|
||||
long Result = IpcHandler.IpcCall(Ns, Process, Memory, Session, Cmd, CmdPtr);
|
||||
|
||||
Thread.Yield();
|
||||
|
||||
Process.Scheduler.Resume(CurrThread);
|
||||
|
||||
ThreadState.X0 = (ulong)Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{Handle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private void SvcBreak(AThreadState ThreadState)
|
||||
{
|
||||
long Reason = (long)ThreadState.X0;
|
||||
long Unknown = (long)ThreadState.X1;
|
||||
long Info = (long)ThreadState.X2;
|
||||
|
||||
Process.PrintStackTrace(ThreadState);
|
||||
|
||||
throw new GuestBrokeExecutionException();
|
||||
}
|
||||
|
||||
private void SvcOutputDebugString(AThreadState ThreadState)
|
||||
{
|
||||
long Position = (long)ThreadState.X0;
|
||||
long Size = (long)ThreadState.X1;
|
||||
|
||||
string Str = AMemoryHelper.ReadAsciiString(Memory, Position, Size);
|
||||
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, Str);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
private void SvcGetInfo(AThreadState ThreadState)
|
||||
{
|
||||
long StackPtr = (long)ThreadState.X0;
|
||||
int InfoType = (int)ThreadState.X1;
|
||||
long Handle = (long)ThreadState.X2;
|
||||
int InfoId = (int)ThreadState.X3;
|
||||
|
||||
//Fail for info not available on older Kernel versions.
|
||||
if (InfoType == 18 ||
|
||||
InfoType == 19 ||
|
||||
InfoType == 20)
|
||||
{
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (InfoType)
|
||||
{
|
||||
case 0:
|
||||
ThreadState.X1 = AllowedCpuIdBitmask;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
ThreadState.X1 = MemoryRegions.MapRegionAddress;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
ThreadState.X1 = MemoryRegions.MapRegionSize;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
ThreadState.X1 = MemoryRegions.HeapRegionAddress;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
ThreadState.X1 = MemoryRegions.HeapRegionSize;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
ThreadState.X1 = MemoryRegions.TotalMemoryAvailable;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
ThreadState.X1 = MemoryRegions.TotalMemoryUsed + CurrentHeapSize;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
ThreadState.X1 = EnableProcessDebugging ? 1 : 0;
|
||||
break;
|
||||
|
||||
case 11:
|
||||
ThreadState.X1 = (ulong)Rng.Next() + ((ulong)Rng.Next() << 32);
|
||||
break;
|
||||
|
||||
case 12:
|
||||
ThreadState.X1 = MemoryRegions.AddrSpaceStart;
|
||||
break;
|
||||
|
||||
case 13:
|
||||
ThreadState.X1 = MemoryRegions.AddrSpaceSize;
|
||||
break;
|
||||
|
||||
case 14:
|
||||
ThreadState.X1 = MemoryRegions.MapRegionAddress;
|
||||
break;
|
||||
|
||||
case 15:
|
||||
ThreadState.X1 = MemoryRegions.MapRegionSize;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
ThreadState.X1 = IsVirtualMemoryEnabled ? 1 : 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
Process.PrintStackTrace(ThreadState);
|
||||
|
||||
throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle:x8} {InfoId}");
|
||||
}
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
}
|
||||
}
|
291
Ryujinx.HLE/OsHle/Kernel/SvcThread.cs
Normal file
291
Ryujinx.HLE/OsHle/Kernel/SvcThread.cs
Normal file
|
@ -0,0 +1,291 @@
|
|||
using ChocolArm64.State;
|
||||
using Ryujinx.HLE.Logging;
|
||||
using Ryujinx.HLE.OsHle.Handles;
|
||||
using System.Threading;
|
||||
|
||||
using static Ryujinx.HLE.OsHle.ErrorCode;
|
||||
|
||||
namespace Ryujinx.HLE.OsHle.Kernel
|
||||
{
|
||||
partial class SvcHandler
|
||||
{
|
||||
private void SvcCreateThread(AThreadState ThreadState)
|
||||
{
|
||||
long EntryPoint = (long)ThreadState.X1;
|
||||
long ArgsPtr = (long)ThreadState.X2;
|
||||
long StackTop = (long)ThreadState.X3;
|
||||
int Priority = (int)ThreadState.X4;
|
||||
int ProcessorId = (int)ThreadState.X5;
|
||||
|
||||
if ((uint)Priority > 0x3f)
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid priority 0x{Priority:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPriority);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (ProcessorId == -2)
|
||||
{
|
||||
//TODO: Get this value from the NPDM file.
|
||||
ProcessorId = 0;
|
||||
}
|
||||
else if ((uint)ProcessorId > 3)
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{ProcessorId:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int Handle = Process.MakeThread(
|
||||
EntryPoint,
|
||||
StackTop,
|
||||
ArgsPtr,
|
||||
Priority,
|
||||
ProcessorId);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
ThreadState.X1 = (ulong)Handle;
|
||||
}
|
||||
|
||||
private void SvcStartThread(AThreadState ThreadState)
|
||||
{
|
||||
int Handle = (int)ThreadState.X0;
|
||||
|
||||
KThread NewThread = Process.HandleTable.GetData<KThread>(Handle);
|
||||
|
||||
if (NewThread != null)
|
||||
{
|
||||
Process.Scheduler.StartThread(NewThread);
|
||||
Process.Scheduler.SetReschedule(NewThread.ProcessorId);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private void SvcExitThread(AThreadState ThreadState)
|
||||
{
|
||||
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
||||
|
||||
CurrThread.Thread.StopExecution();
|
||||
}
|
||||
|
||||
private void SvcSleepThread(AThreadState ThreadState)
|
||||
{
|
||||
ulong TimeoutNs = ThreadState.X0;
|
||||
|
||||
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
||||
|
||||
if (TimeoutNs == 0)
|
||||
{
|
||||
Process.Scheduler.Yield(CurrThread);
|
||||
}
|
||||
else
|
||||
{
|
||||
Process.Scheduler.Suspend(CurrThread);
|
||||
|
||||
Thread.Sleep(NsTimeConverter.GetTimeMs(TimeoutNs));
|
||||
|
||||
Process.Scheduler.Resume(CurrThread);
|
||||
}
|
||||
}
|
||||
|
||||
private void SvcGetThreadPriority(AThreadState ThreadState)
|
||||
{
|
||||
int Handle = (int)ThreadState.X1;
|
||||
|
||||
KThread Thread = GetThread(ThreadState.Tpidr, Handle);
|
||||
|
||||
if (Thread != null)
|
||||
{
|
||||
ThreadState.X0 = 0;
|
||||
ThreadState.X1 = (ulong)Thread.ActualPriority;
|
||||
}
|
||||
else
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private void SvcSetThreadPriority(AThreadState ThreadState)
|
||||
{
|
||||
int Handle = (int)ThreadState.X0;
|
||||
int Priority = (int)ThreadState.X1;
|
||||
|
||||
KThread Thread = GetThread(ThreadState.Tpidr, Handle);
|
||||
|
||||
if (Thread != null)
|
||||
{
|
||||
Thread.SetPriority(Priority);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private void SvcGetThreadCoreMask(AThreadState ThreadState)
|
||||
{
|
||||
int Handle = (int)ThreadState.X2;
|
||||
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc, "Handle = " + Handle.ToString("x8"));
|
||||
|
||||
KThread Thread = GetThread(ThreadState.Tpidr, Handle);
|
||||
|
||||
if (Thread != null)
|
||||
{
|
||||
ThreadState.X0 = 0;
|
||||
ThreadState.X1 = (ulong)Thread.IdealCore;
|
||||
ThreadState.X2 = (ulong)Thread.CoreMask;
|
||||
}
|
||||
else
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private void SvcSetThreadCoreMask(AThreadState ThreadState)
|
||||
{
|
||||
//FIXME: This is wrong, but the "correct" way to handle
|
||||
//this svc causes deadlocks when more often.
|
||||
//There is probably something wrong with it still.
|
||||
ThreadState.X0 = 0;
|
||||
|
||||
return;
|
||||
|
||||
int Handle = (int)ThreadState.X0;
|
||||
int IdealCore = (int)ThreadState.X1;
|
||||
long CoreMask = (long)ThreadState.X2;
|
||||
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc,
|
||||
"Handle = " + Handle .ToString("x8") + ", " +
|
||||
"IdealCore = " + IdealCore.ToString("x8") + ", " +
|
||||
"CoreMask = " + CoreMask .ToString("x16"));
|
||||
|
||||
KThread Thread = GetThread(ThreadState.Tpidr, Handle);
|
||||
|
||||
if (IdealCore == -2)
|
||||
{
|
||||
//TODO: Get this value from the NPDM file.
|
||||
IdealCore = 0;
|
||||
|
||||
CoreMask = 1 << IdealCore;
|
||||
}
|
||||
else if (IdealCore != -3)
|
||||
{
|
||||
if ((uint)IdealCore > 3)
|
||||
{
|
||||
if ((IdealCore | 2) != -1)
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{IdealCore:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ((CoreMask & (1 << IdealCore)) == 0)
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{CoreMask:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreMask);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Thread == null)
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//-1 is used as "don't care", so the IdealCore value is ignored.
|
||||
//-2 is used as "use NPDM default core id" (handled above).
|
||||
//-3 is used as "don't update", the old IdealCore value is kept.
|
||||
if (IdealCore != -3)
|
||||
{
|
||||
Thread.IdealCore = IdealCore;
|
||||
}
|
||||
else if ((CoreMask & (1 << Thread.IdealCore)) == 0)
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{CoreMask:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreMask);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Thread.CoreMask = (int)CoreMask;
|
||||
|
||||
Process.Scheduler.TryToRun(Thread);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
private void SvcGetCurrentProcessorNumber(AThreadState ThreadState)
|
||||
{
|
||||
ThreadState.X0 = (ulong)Process.GetThread(ThreadState.Tpidr).ActualCore;
|
||||
}
|
||||
|
||||
private void SvcGetThreadId(AThreadState ThreadState)
|
||||
{
|
||||
int Handle = (int)ThreadState.X1;
|
||||
|
||||
KThread Thread = GetThread(ThreadState.Tpidr, Handle);
|
||||
|
||||
if (Thread != null)
|
||||
{
|
||||
ThreadState.X0 = 0;
|
||||
ThreadState.X1 = (ulong)Thread.ThreadId;
|
||||
}
|
||||
else
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private void SvcSetThreadActivity(AThreadState ThreadState)
|
||||
{
|
||||
int Handle = (int)ThreadState.X0;
|
||||
bool Active = (int)ThreadState.X1 == 0;
|
||||
|
||||
KThread Thread = Process.HandleTable.GetData<KThread>(Handle);
|
||||
|
||||
if (Thread != null)
|
||||
{
|
||||
Process.Scheduler.SetThreadActivity(Thread, Active);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
435
Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs
Normal file
435
Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs
Normal file
|
@ -0,0 +1,435 @@
|
|||
using ChocolArm64.State;
|
||||
using Ryujinx.HLE.Logging;
|
||||
using Ryujinx.HLE.OsHle.Handles;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
using static Ryujinx.HLE.OsHle.ErrorCode;
|
||||
|
||||
namespace Ryujinx.HLE.OsHle.Kernel
|
||||
{
|
||||
partial class SvcHandler
|
||||
{
|
||||
private const int MutexHasListenersMask = 0x40000000;
|
||||
|
||||
private void SvcArbitrateLock(AThreadState ThreadState)
|
||||
{
|
||||
int OwnerThreadHandle = (int)ThreadState.X0;
|
||||
long MutexAddress = (long)ThreadState.X1;
|
||||
int WaitThreadHandle = (int)ThreadState.X2;
|
||||
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc,
|
||||
"OwnerThreadHandle = " + OwnerThreadHandle.ToString("x8") + ", " +
|
||||
"MutexAddress = " + MutexAddress .ToString("x16") + ", " +
|
||||
"WaitThreadHandle = " + WaitThreadHandle .ToString("x8"));
|
||||
|
||||
if (IsPointingInsideKernel(MutexAddress))
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsWordAddressUnaligned(MutexAddress))
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
KThread OwnerThread = Process.HandleTable.GetData<KThread>(OwnerThreadHandle);
|
||||
|
||||
if (OwnerThread == null)
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid owner thread handle 0x{OwnerThreadHandle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
KThread WaitThread = Process.HandleTable.GetData<KThread>(WaitThreadHandle);
|
||||
|
||||
if (WaitThread == null)
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid requesting thread handle 0x{WaitThreadHandle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
||||
|
||||
MutexLock(CurrThread, WaitThread, OwnerThreadHandle, WaitThreadHandle, MutexAddress);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
private void SvcArbitrateUnlock(AThreadState ThreadState)
|
||||
{
|
||||
long MutexAddress = (long)ThreadState.X0;
|
||||
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexAddress = " + MutexAddress.ToString("x16"));
|
||||
|
||||
if (IsPointingInsideKernel(MutexAddress))
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsWordAddressUnaligned(MutexAddress))
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
MutexUnlock(Process.GetThread(ThreadState.Tpidr), MutexAddress);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState)
|
||||
{
|
||||
long MutexAddress = (long)ThreadState.X0;
|
||||
long CondVarAddress = (long)ThreadState.X1;
|
||||
int ThreadHandle = (int)ThreadState.X2;
|
||||
ulong Timeout = ThreadState.X3;
|
||||
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc,
|
||||
"MutexAddress = " + MutexAddress .ToString("x16") + ", " +
|
||||
"CondVarAddress = " + CondVarAddress.ToString("x16") + ", " +
|
||||
"ThreadHandle = " + ThreadHandle .ToString("x8") + ", " +
|
||||
"Timeout = " + Timeout .ToString("x16"));
|
||||
|
||||
if (IsPointingInsideKernel(MutexAddress))
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsWordAddressUnaligned(MutexAddress))
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
KThread Thread = Process.HandleTable.GetData<KThread>(ThreadHandle);
|
||||
|
||||
if (Thread == null)
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!");
|
||||
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
||||
|
||||
MutexUnlock(CurrThread, MutexAddress);
|
||||
|
||||
if (!CondVarWait(CurrThread, ThreadHandle, MutexAddress, CondVarAddress, Timeout))
|
||||
{
|
||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
private void SvcSignalProcessWideKey(AThreadState ThreadState)
|
||||
{
|
||||
long CondVarAddress = (long)ThreadState.X0;
|
||||
int Count = (int)ThreadState.X1;
|
||||
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc,
|
||||
"CondVarAddress = " + CondVarAddress.ToString("x16") + ", " +
|
||||
"Count = " + Count .ToString("x8"));
|
||||
|
||||
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
||||
|
||||
CondVarSignal(CurrThread, CondVarAddress, Count);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
||||
private void MutexLock(
|
||||
KThread CurrThread,
|
||||
KThread WaitThread,
|
||||
int OwnerThreadHandle,
|
||||
int WaitThreadHandle,
|
||||
long MutexAddress)
|
||||
{
|
||||
lock (Process.ThreadSyncLock)
|
||||
{
|
||||
int MutexValue = Process.Memory.ReadInt32(MutexAddress);
|
||||
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = " + MutexValue.ToString("x8"));
|
||||
|
||||
if (MutexValue != (OwnerThreadHandle | MutexHasListenersMask))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrThread.WaitHandle = WaitThreadHandle;
|
||||
CurrThread.MutexAddress = MutexAddress;
|
||||
|
||||
InsertWaitingMutexThread(OwnerThreadHandle, WaitThread);
|
||||
}
|
||||
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state...");
|
||||
|
||||
Process.Scheduler.EnterWait(CurrThread);
|
||||
}
|
||||
|
||||
private void MutexUnlock(KThread CurrThread, long MutexAddress)
|
||||
{
|
||||
lock (Process.ThreadSyncLock)
|
||||
{
|
||||
//This is the new thread that will now own the mutex.
|
||||
//If no threads are waiting for the lock, then it should be null.
|
||||
KThread OwnerThread = PopThread(CurrThread.MutexWaiters, x => x.MutexAddress == MutexAddress);
|
||||
|
||||
if (OwnerThread != null)
|
||||
{
|
||||
//Remove all waiting mutex from the old owner,
|
||||
//and insert then on the new owner.
|
||||
UpdateMutexOwner(CurrThread, OwnerThread, MutexAddress);
|
||||
|
||||
CurrThread.UpdatePriority();
|
||||
|
||||
int HasListeners = OwnerThread.MutexWaiters.Count > 0 ? MutexHasListenersMask : 0;
|
||||
|
||||
Process.Memory.WriteInt32(MutexAddress, HasListeners | OwnerThread.WaitHandle);
|
||||
|
||||
OwnerThread.WaitHandle = 0;
|
||||
OwnerThread.MutexAddress = 0;
|
||||
OwnerThread.CondVarAddress = 0;
|
||||
|
||||
OwnerThread.MutexOwner = null;
|
||||
|
||||
OwnerThread.UpdatePriority();
|
||||
|
||||
Process.Scheduler.WakeUp(OwnerThread);
|
||||
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc, "Gave mutex to thread id " + OwnerThread.ThreadId + "!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Process.Memory.WriteInt32(MutexAddress, 0);
|
||||
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc, "No threads waiting mutex!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool CondVarWait(
|
||||
KThread WaitThread,
|
||||
int WaitThreadHandle,
|
||||
long MutexAddress,
|
||||
long CondVarAddress,
|
||||
ulong Timeout)
|
||||
{
|
||||
WaitThread.WaitHandle = WaitThreadHandle;
|
||||
WaitThread.MutexAddress = MutexAddress;
|
||||
WaitThread.CondVarAddress = CondVarAddress;
|
||||
|
||||
lock (Process.ThreadSyncLock)
|
||||
{
|
||||
WaitThread.CondVarSignaled = false;
|
||||
|
||||
Process.ThreadArbiterList.Add(WaitThread);
|
||||
}
|
||||
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state...");
|
||||
|
||||
if (Timeout != ulong.MaxValue)
|
||||
{
|
||||
Process.Scheduler.EnterWait(WaitThread, NsTimeConverter.GetTimeMs(Timeout));
|
||||
|
||||
lock (Process.ThreadSyncLock)
|
||||
{
|
||||
WaitThread.MutexOwner?.MutexWaiters.Remove(WaitThread);
|
||||
|
||||
if (!WaitThread.CondVarSignaled || WaitThread.MutexOwner != null)
|
||||
{
|
||||
WaitThread.MutexOwner = null;
|
||||
|
||||
Process.ThreadArbiterList.Remove(WaitThread);
|
||||
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc, "Timed out...");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Process.Scheduler.EnterWait(WaitThread);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CondVarSignal(KThread CurrThread, long CondVarAddress, int Count)
|
||||
{
|
||||
lock (Process.ThreadSyncLock)
|
||||
{
|
||||
while (Count == -1 || Count-- > 0)
|
||||
{
|
||||
KThread WaitThread = PopThread(Process.ThreadArbiterList, x => x.CondVarAddress == CondVarAddress);
|
||||
|
||||
if (WaitThread == null)
|
||||
{
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc, "No more threads to wake up!");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
WaitThread.CondVarSignaled = true;
|
||||
|
||||
AcquireMutexValue(WaitThread.MutexAddress);
|
||||
|
||||
int MutexValue = Process.Memory.ReadInt32(WaitThread.MutexAddress);
|
||||
|
||||
Ns.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = " + MutexValue.ToString("x8"));
|
||||
|
||||
if (MutexValue == 0)
|
||||
{
|
||||
//Give the lock to this thread.
|
||||
Process.Memory.WriteInt32(WaitThread.MutexAddress, WaitThread.WaitHandle);
|
||||
|
||||
WaitThread.WaitHandle = 0;
|
||||
WaitThread.MutexAddress = 0;
|
||||
WaitThread.CondVarAddress = 0;
|
||||
|
||||
WaitThread.MutexOwner?.UpdatePriority();
|
||||
|
||||
WaitThread.MutexOwner = null;
|
||||
|
||||
Process.Scheduler.WakeUp(WaitThread);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Wait until the lock is released.
|
||||
MutexValue &= ~MutexHasListenersMask;
|
||||
|
||||
InsertWaitingMutexThread(MutexValue, WaitThread);
|
||||
|
||||
MutexValue |= MutexHasListenersMask;
|
||||
|
||||
Process.Memory.WriteInt32(WaitThread.MutexAddress, MutexValue);
|
||||
}
|
||||
|
||||
ReleaseMutexValue(WaitThread.MutexAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMutexOwner(KThread CurrThread, KThread NewOwner, long MutexAddress)
|
||||
{
|
||||
//Go through all threads waiting for the mutex,
|
||||
//and update the MutexOwner field to point to the new owner.
|
||||
lock (Process.ThreadSyncLock)
|
||||
{
|
||||
for (int Index = 0; Index < CurrThread.MutexWaiters.Count; Index++)
|
||||
{
|
||||
KThread Thread = CurrThread.MutexWaiters[Index];
|
||||
|
||||
if (Thread.MutexAddress == MutexAddress)
|
||||
{
|
||||
CurrThread.MutexWaiters.RemoveAt(Index--);
|
||||
|
||||
Thread.MutexOwner = NewOwner;
|
||||
|
||||
InsertWaitingMutexThread(NewOwner, Thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InsertWaitingMutexThread(int OwnerThreadHandle, KThread WaitThread)
|
||||
{
|
||||
KThread OwnerThread = Process.HandleTable.GetData<KThread>(OwnerThreadHandle);
|
||||
|
||||
if (OwnerThread == null)
|
||||
{
|
||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{OwnerThreadHandle:x8}!");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
InsertWaitingMutexThread(OwnerThread, WaitThread);
|
||||
}
|
||||
|
||||
private void InsertWaitingMutexThread(KThread OwnerThread, KThread WaitThread)
|
||||
{
|
||||
lock (Process.ThreadSyncLock)
|
||||
{
|
||||
WaitThread.MutexOwner = OwnerThread;
|
||||
|
||||
if (!OwnerThread.MutexWaiters.Contains(WaitThread))
|
||||
{
|
||||
OwnerThread.MutexWaiters.Add(WaitThread);
|
||||
|
||||
OwnerThread.UpdatePriority();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private KThread PopThread(List<KThread> Threads, Func<KThread, bool> Predicate)
|
||||
{
|
||||
KThread Thread = Threads.OrderBy(x => x.ActualPriority).FirstOrDefault(Predicate);
|
||||
|
||||
if (Thread != null)
|
||||
{
|
||||
Threads.Remove(Thread);
|
||||
}
|
||||
|
||||
return Thread;
|
||||
}
|
||||
|
||||
private void AcquireMutexValue(long MutexAddress)
|
||||
{
|
||||
while (!Process.Memory.AcquireAddress(MutexAddress))
|
||||
{
|
||||
Thread.Yield();
|
||||
}
|
||||
}
|
||||
|
||||
private void ReleaseMutexValue(long MutexAddress)
|
||||
{
|
||||
Process.Memory.ReleaseAddress(MutexAddress);
|
||||
}
|
||||
|
||||
private bool IsPointingInsideKernel(long Address)
|
||||
{
|
||||
return ((ulong)Address + 0x1000000000) < 0xffffff000;
|
||||
}
|
||||
|
||||
private bool IsWordAddressUnaligned(long Address)
|
||||
{
|
||||
return (Address & 3) != 0;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue