aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Kernel/SupervisorCall
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2018-12-18 03:33:36 -0200
committerGitHub <noreply@github.com>2018-12-18 03:33:36 -0200
commit0039bb639493b2d1e2764cae380311ba8e87704b (patch)
tree63a912a95c8261775c2acb8a5b9ca0f10ad4ae33 /Ryujinx.HLE/HOS/Kernel/SupervisorCall
parent2534a7f10c627810e6e0272b4cc9758e90f733c1 (diff)
Refactor SVC handler (#540)
* Refactor SVC handler * Get rid of KernelErr * Split kernel code files into multiple folders
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/SupervisorCall')
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/InvalidSvcException.cs9
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs61
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs394
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs762
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs340
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs420
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThreadSync.cs250
7 files changed, 2236 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/InvalidSvcException.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/InvalidSvcException.cs
new file mode 100644
index 00000000..b78a2284
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/InvalidSvcException.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
+{
+ public class InvalidSvcException : Exception
+ {
+ public InvalidSvcException(string message) : base(message) { }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs
new file mode 100644
index 00000000..08340b06
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs
@@ -0,0 +1,61 @@
+using ChocolArm64.Events;
+using ChocolArm64.Memory;
+using ChocolArm64.State;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using System;
+
+namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
+{
+ partial class SvcHandler
+ {
+ private Switch _device;
+ private KProcess _process;
+ private Horizon _system;
+ private MemoryManager _memory;
+
+ private struct HleIpcMessage
+ {
+ public KThread Thread { get; private set; }
+ public KSession Session { get; private set; }
+ public IpcMessage Message { get; private set; }
+ public long MessagePtr { get; private set; }
+
+ public HleIpcMessage(
+ KThread thread,
+ KSession session,
+ IpcMessage message,
+ long messagePtr)
+ {
+ Thread = thread;
+ Session = session;
+ Message = message;
+ MessagePtr = messagePtr;
+ }
+ }
+
+ public SvcHandler(Switch device, KProcess process)
+ {
+ _device = device;
+ _process = process;
+ _system = device.System;
+ _memory = process.CpuMemory;
+ }
+
+ public void SvcCall(object sender, InstExceptionEventArgs e)
+ {
+ Action<SvcHandler, CpuThreadState> svcFunc = SvcTable.GetSvcFunc(e.Id);
+
+ if (svcFunc == null)
+ {
+ throw new NotImplementedException($"SVC 0x{e.Id:X4} is not implemented.");
+ }
+
+ CpuThreadState threadState = (CpuThreadState)sender;
+
+ svcFunc(this, threadState);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs
new file mode 100644
index 00000000..e6590522
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs
@@ -0,0 +1,394 @@
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Memory;
+using Ryujinx.HLE.HOS.Kernel.Process;
+
+namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
+{
+ partial class SvcHandler
+ {
+ public KernelResult SetHeapSize64(ulong size, out ulong position)
+ {
+ return SetHeapSize(size, out position);
+ }
+
+ private KernelResult SetHeapSize(ulong size, out ulong position)
+ {
+ if ((size & 0xfffffffe001fffff) != 0)
+ {
+ position = 0;
+
+ return KernelResult.InvalidSize;
+ }
+
+ return _process.MemoryManager.SetHeapSize(size, out position);
+ }
+
+ public KernelResult SetMemoryAttribute64(
+ ulong position,
+ ulong size,
+ MemoryAttribute attributeMask,
+ MemoryAttribute attributeValue)
+ {
+ return SetMemoryAttribute(position, size, attributeMask, attributeValue);
+ }
+
+ private KernelResult SetMemoryAttribute(
+ ulong position,
+ ulong size,
+ MemoryAttribute attributeMask,
+ MemoryAttribute attributeValue)
+ {
+ if (!PageAligned(position))
+ {
+ return KernelResult.InvalidAddress;
+ }
+
+ if (!PageAligned(size) || size == 0)
+ {
+ return KernelResult.InvalidSize;
+ }
+
+ MemoryAttribute attributes = attributeMask | attributeValue;
+
+ if (attributes != attributeMask ||
+ (attributes | MemoryAttribute.Uncached) != MemoryAttribute.Uncached)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ KernelResult result = _process.MemoryManager.SetMemoryAttribute(
+ position,
+ size,
+ attributeMask,
+ attributeValue);
+
+ if (result == KernelResult.Success)
+ {
+ _memory.StopObservingRegion((long)position, (long)size);
+ }
+
+ return result;
+ }
+
+ public KernelResult MapMemory64(ulong dst, ulong src, ulong size)
+ {
+ return MapMemory(dst, src, size);
+ }
+
+ private KernelResult MapMemory(ulong dst, ulong src, ulong size)
+ {
+ if (!PageAligned(src | dst))
+ {
+ return KernelResult.InvalidAddress;
+ }
+
+ if (!PageAligned(size) || size == 0)
+ {
+ return KernelResult.InvalidSize;
+ }
+
+ if (src + size <= src || dst + size <= dst)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ if (!currentProcess.MemoryManager.InsideAddrSpace(src, size))
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (currentProcess.MemoryManager.OutsideStackRegion(dst, size) ||
+ currentProcess.MemoryManager.InsideHeapRegion (dst, size) ||
+ currentProcess.MemoryManager.InsideAliasRegion (dst, size))
+ {
+ return KernelResult.InvalidMemRange;
+ }
+
+ return _process.MemoryManager.Map(dst, src, size);
+ }
+
+ public KernelResult UnmapMemory64(ulong dst, ulong src, ulong size)
+ {
+ return UnmapMemory(dst, src, size);
+ }
+
+ private KernelResult UnmapMemory(ulong dst, ulong src, ulong size)
+ {
+ if (!PageAligned(src | dst))
+ {
+ return KernelResult.InvalidAddress;
+ }
+
+ if (!PageAligned(size) || size == 0)
+ {
+ return KernelResult.InvalidSize;
+ }
+
+ if (src + size <= src || dst + size <= dst)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ if (!currentProcess.MemoryManager.InsideAddrSpace(src, size))
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (currentProcess.MemoryManager.OutsideStackRegion(dst, size) ||
+ currentProcess.MemoryManager.InsideHeapRegion (dst, size) ||
+ currentProcess.MemoryManager.InsideAliasRegion (dst, size))
+ {
+ return KernelResult.InvalidMemRange;
+ }
+
+ return _process.MemoryManager.Unmap(dst, src, size);
+ }
+
+ public KernelResult QueryMemory64(ulong infoPtr, ulong x1, ulong position)
+ {
+ return QueryMemory(infoPtr, position);
+ }
+
+ private KernelResult QueryMemory(ulong infoPtr, ulong position)
+ {
+ KMemoryInfo blkInfo = _process.MemoryManager.QueryMemory(position);
+
+ _memory.WriteUInt64((long)infoPtr + 0x00, blkInfo.Address);
+ _memory.WriteUInt64((long)infoPtr + 0x08, blkInfo.Size);
+ _memory.WriteInt32 ((long)infoPtr + 0x10, (int)blkInfo.State & 0xff);
+ _memory.WriteInt32 ((long)infoPtr + 0x14, (int)blkInfo.Attribute);
+ _memory.WriteInt32 ((long)infoPtr + 0x18, (int)blkInfo.Permission);
+ _memory.WriteInt32 ((long)infoPtr + 0x1c, blkInfo.IpcRefCount);
+ _memory.WriteInt32 ((long)infoPtr + 0x20, blkInfo.DeviceRefCount);
+ _memory.WriteInt32 ((long)infoPtr + 0x24, 0);
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult MapSharedMemory64(int handle, ulong address, ulong size, MemoryPermission permission)
+ {
+ return MapSharedMemory(handle, address, size, permission);
+ }
+
+ private KernelResult MapSharedMemory(int handle, ulong address, ulong size, MemoryPermission permission)
+ {
+ if (!PageAligned(address))
+ {
+ return KernelResult.InvalidAddress;
+ }
+
+ if (!PageAligned(size) || size == 0)
+ {
+ return KernelResult.InvalidSize;
+ }
+
+ if (address + size <= address)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if ((permission | MemoryPermission.Write) != MemoryPermission.ReadAndWrite)
+ {
+ return KernelResult.InvalidPermission;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle);
+
+ if (sharedMemory == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ if (currentProcess.MemoryManager.IsInvalidRegion (address, size) ||
+ currentProcess.MemoryManager.InsideHeapRegion (address, size) ||
+ currentProcess.MemoryManager.InsideAliasRegion(address, size))
+ {
+ return KernelResult.InvalidMemRange;
+ }
+
+ return sharedMemory.MapIntoProcess(
+ currentProcess.MemoryManager,
+ address,
+ size,
+ currentProcess,
+ permission);
+ }
+
+ public KernelResult UnmapSharedMemory64(int handle, ulong address, ulong size)
+ {
+ return UnmapSharedMemory(handle, address, size);
+ }
+
+ private KernelResult UnmapSharedMemory(int handle, ulong address, ulong size)
+ {
+ if (!PageAligned(address))
+ {
+ return KernelResult.InvalidAddress;
+ }
+
+ if (!PageAligned(size) || size == 0)
+ {
+ return KernelResult.InvalidSize;
+ }
+
+ if (address + size <= address)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KSharedMemory sharedMemory = currentProcess.HandleTable.GetObject<KSharedMemory>(handle);
+
+ if (sharedMemory == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ if (currentProcess.MemoryManager.IsInvalidRegion (address, size) ||
+ currentProcess.MemoryManager.InsideHeapRegion (address, size) ||
+ currentProcess.MemoryManager.InsideAliasRegion(address, size))
+ {
+ return KernelResult.InvalidMemRange;
+ }
+
+ return sharedMemory.UnmapFromProcess(
+ currentProcess.MemoryManager,
+ address,
+ size,
+ currentProcess);
+ }
+
+ public KernelResult CreateTransferMemory64(
+ ulong address,
+ ulong size,
+ MemoryPermission permission,
+ out int handle)
+ {
+ return CreateTransferMemory(address, size, permission, out handle);
+ }
+
+ private KernelResult CreateTransferMemory(ulong address, ulong size, MemoryPermission permission, out int handle)
+ {
+ handle = 0;
+
+ if (!PageAligned(address))
+ {
+ return KernelResult.InvalidAddress;
+ }
+
+ if (!PageAligned(size) || size == 0)
+ {
+ return KernelResult.InvalidSize;
+ }
+
+ if (address + size <= address)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (permission > MemoryPermission.ReadAndWrite || permission == MemoryPermission.Write)
+ {
+ return KernelResult.InvalidPermission;
+ }
+
+ KernelResult result = _process.MemoryManager.ReserveTransferMemory(address, size, permission);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ KTransferMemory transferMemory = new KTransferMemory(address, size);
+
+ return _process.HandleTable.GenerateHandle(transferMemory, out handle);
+ }
+
+ public KernelResult MapPhysicalMemory64(ulong address, ulong size)
+ {
+ return MapPhysicalMemory(address, size);
+ }
+
+ private KernelResult MapPhysicalMemory(ulong address, ulong size)
+ {
+ if (!PageAligned(address))
+ {
+ return KernelResult.InvalidAddress;
+ }
+
+ if (!PageAligned(size) || size == 0)
+ {
+ return KernelResult.InvalidSize;
+ }
+
+ if (address + size <= address)
+ {
+ return KernelResult.InvalidMemRange;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
+ {
+ return KernelResult.InvalidState;
+ }
+
+ if (!currentProcess.MemoryManager.InsideAddrSpace (address, size) ||
+ currentProcess.MemoryManager.OutsideAliasRegion(address, size))
+ {
+ return KernelResult.InvalidMemRange;
+ }
+
+ return _process.MemoryManager.MapPhysicalMemory(address, size);
+ }
+
+ public KernelResult UnmapPhysicalMemory64(ulong address, ulong size)
+ {
+ return MapPhysicalMemory(address, size);
+ }
+
+ private KernelResult UnmapPhysicalMemory(ulong address, ulong size)
+ {
+ if (!PageAligned(address))
+ {
+ return KernelResult.InvalidAddress;
+ }
+
+ if (!PageAligned(size) || size == 0)
+ {
+ return KernelResult.InvalidSize;
+ }
+
+ if (address + size <= address)
+ {
+ return KernelResult.InvalidMemRange;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ if ((currentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
+ {
+ return KernelResult.InvalidState;
+ }
+
+ if (!currentProcess.MemoryManager.InsideAddrSpace (address, size) ||
+ currentProcess.MemoryManager.OutsideAliasRegion(address, size))
+ {
+ return KernelResult.InvalidMemRange;
+ }
+
+ return _process.MemoryManager.UnmapPhysicalMemory(address, size);
+ }
+
+ private static bool PageAligned(ulong position)
+ {
+ return (position & (KMemoryManager.PageSize - 1)) == 0;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs
new file mode 100644
index 00000000..b0563356
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs
@@ -0,0 +1,762 @@
+using ChocolArm64.Memory;
+using Ryujinx.Common;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.Exceptions;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Memory;
+using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.HLE.HOS.Services;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
+{
+ partial class SvcHandler
+ {
+ public void ExitProcess64()
+ {
+ ExitProcess();
+ }
+
+ private void ExitProcess()
+ {
+ _system.Scheduler.GetCurrentProcess().Terminate();
+ }
+
+ public KernelResult SignalEvent64(int handle)
+ {
+ return SignalEvent(handle);
+ }
+
+ private KernelResult SignalEvent(int handle)
+ {
+ KWritableEvent writableEvent = _process.HandleTable.GetObject<KWritableEvent>(handle);
+
+ KernelResult result;
+
+ if (writableEvent != null)
+ {
+ writableEvent.Signal();
+
+ result = KernelResult.Success;
+ }
+ else
+ {
+ result = KernelResult.InvalidHandle;
+ }
+
+ return result;
+ }
+
+ public KernelResult ClearEvent64(int handle)
+ {
+ return ClearEvent(handle);
+ }
+
+ private KernelResult ClearEvent(int handle)
+ {
+ KernelResult result;
+
+ KWritableEvent writableEvent = _process.HandleTable.GetObject<KWritableEvent>(handle);
+
+ if (writableEvent == null)
+ {
+ KReadableEvent readableEvent = _process.HandleTable.GetObject<KReadableEvent>(handle);
+
+ result = readableEvent?.Clear() ?? KernelResult.InvalidHandle;
+ }
+ else
+ {
+ result = writableEvent.Clear();
+ }
+
+ return result;
+ }
+
+ public KernelResult CloseHandle64(int handle)
+ {
+ return CloseHandle(handle);
+ }
+
+ private KernelResult CloseHandle(int handle)
+ {
+ object obj = _process.HandleTable.GetObject<object>(handle);
+
+ _process.HandleTable.CloseHandle(handle);
+
+ if (obj == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ if (obj is KSession session)
+ {
+ session.Dispose();
+ }
+ else if (obj is KTransferMemory transferMemory)
+ {
+ _process.MemoryManager.ResetTransferMemory(
+ transferMemory.Address,
+ transferMemory.Size);
+ }
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult ResetSignal64(int handle)
+ {
+ return ResetSignal(handle);
+ }
+
+ private KernelResult ResetSignal(int handle)
+ {
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KReadableEvent readableEvent = currentProcess.HandleTable.GetObject<KReadableEvent>(handle);
+
+ KernelResult result;
+
+ if (readableEvent != null)
+ {
+ result = readableEvent.ClearIfSignaled();
+ }
+ else
+ {
+ KProcess process = currentProcess.HandleTable.GetKProcess(handle);
+
+ if (process != null)
+ {
+ result = process.ClearIfNotExited();
+ }
+ else
+ {
+ result = KernelResult.InvalidHandle;
+ }
+ }
+
+ return result;
+ }
+
+ public ulong GetSystemTick64()
+ {
+ return _system.Scheduler.GetCurrentThread().Context.ThreadState.CntpctEl0;
+ }
+
+ public KernelResult ConnectToNamedPort64(ulong namePtr, out int handle)
+ {
+ return ConnectToNamedPort(namePtr, out handle);
+ }
+
+ private KernelResult ConnectToNamedPort(ulong namePtr, out int handle)
+ {
+ string name = MemoryHelper.ReadAsciiString(_memory, (long)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(_system, name), name);
+
+ return _process.HandleTable.GenerateHandle(session, out handle);
+ }
+
+ public KernelResult SendSyncRequest64(int handle)
+ {
+ return SendSyncRequest((ulong)_system.Scheduler.GetCurrentThread().Context.ThreadState.Tpidr, 0x100, handle);
+ }
+
+ public KernelResult SendSyncRequestWithUserBuffer64(ulong messagePtr, ulong size, int handle)
+ {
+ return SendSyncRequest(messagePtr, size, handle);
+ }
+
+ private KernelResult SendSyncRequest(ulong messagePtr, ulong size, int handle)
+ {
+ byte[] messageData = _memory.ReadBytes((long)messagePtr, (long)size);
+
+ KSession session = _process.HandleTable.GetObject<KSession>(handle);
+
+ if (session != null)
+ {
+ _system.CriticalSection.Enter();
+
+ KThread currentThread = _system.Scheduler.GetCurrentThread();
+
+ currentThread.SignaledObj = null;
+ currentThread.ObjSyncResult = KernelResult.Success;
+
+ currentThread.Reschedule(ThreadSchedState.Paused);
+
+ IpcMessage message = new IpcMessage(messageData, (long)messagePtr);
+
+ ThreadPool.QueueUserWorkItem(ProcessIpcRequest, new HleIpcMessage(
+ currentThread,
+ session,
+ message,
+ (long)messagePtr));
+
+ _system.ThreadCounter.AddCount();
+
+ _system.CriticalSection.Leave();
+
+ return currentThread.ObjSyncResult;
+ }
+ else
+ {
+ Logger.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{handle:x8}!");
+
+ return KernelResult.InvalidHandle;
+ }
+ }
+
+ private void ProcessIpcRequest(object state)
+ {
+ HleIpcMessage ipcMessage = (HleIpcMessage)state;
+
+ ipcMessage.Thread.ObjSyncResult = IpcHandler.IpcCall(
+ _device,
+ _process,
+ _memory,
+ ipcMessage.Session,
+ ipcMessage.Message,
+ ipcMessage.MessagePtr);
+
+ _system.ThreadCounter.Signal();
+
+ ipcMessage.Thread.Reschedule(ThreadSchedState.Running);
+ }
+
+ public KernelResult GetProcessId64(int handle, out long pid)
+ {
+ return GetProcessId(handle, out pid);
+ }
+
+ private KernelResult GetProcessId(int handle, out long pid)
+ {
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KProcess process = currentProcess.HandleTable.GetKProcess(handle);
+
+ if (process == null)
+ {
+ KThread thread = currentProcess.HandleTable.GetKThread(handle);
+
+ if (thread != null)
+ {
+ process = thread.Owner;
+ }
+
+ //TODO: KDebugEvent.
+ }
+
+ pid = process?.Pid ?? 0;
+
+ return process != null
+ ? KernelResult.Success
+ : KernelResult.InvalidHandle;
+ }
+
+ public void Break64(ulong reason, ulong x1, ulong info)
+ {
+ Break(reason);
+ }
+
+ private void Break(ulong reason)
+ {
+ KThread currentThread = _system.Scheduler.GetCurrentThread();
+
+ if ((reason & (1UL << 31)) == 0)
+ {
+ currentThread.PrintGuestStackTrace();
+
+ throw new GuestBrokeExecutionException();
+ }
+ else
+ {
+ Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered.");
+
+ currentThread.PrintGuestStackTrace();
+ }
+ }
+
+ public void OutputDebugString64(ulong strPtr, ulong size)
+ {
+ OutputDebugString(strPtr, size);
+ }
+
+ private void OutputDebugString(ulong strPtr, ulong size)
+ {
+ string str = MemoryHelper.ReadAsciiString(_memory, (long)strPtr, (long)size);
+
+ Logger.PrintWarning(LogClass.KernelSvc, str);
+ }
+
+ public KernelResult GetInfo64(uint id, int handle, long subId, out long value)
+ {
+ return GetInfo(id, handle, subId, out value);
+ }
+
+ private KernelResult GetInfo(uint id, int handle, long subId, out long value)
+ {
+ value = 0;
+
+ switch (id)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ case 20:
+ case 21:
+ case 22:
+ {
+ if (subId != 0)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KProcess process = currentProcess.HandleTable.GetKProcess(handle);
+
+ if (process == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ switch (id)
+ {
+ case 0: value = process.Capabilities.AllowedCpuCoresMask; break;
+ case 1: value = process.Capabilities.AllowedThreadPriosMask; break;
+
+ case 2: value = (long)process.MemoryManager.AliasRegionStart; break;
+ case 3: value = (long)(process.MemoryManager.AliasRegionEnd -
+ process.MemoryManager.AliasRegionStart); break;
+
+ case 4: value = (long)process.MemoryManager.HeapRegionStart; break;
+ case 5: value = (long)(process.MemoryManager.HeapRegionEnd -
+ process.MemoryManager.HeapRegionStart); break;
+
+ case 6: value = (long)process.GetMemoryCapacity(); break;
+
+ case 7: value = (long)process.GetMemoryUsage(); break;
+
+ case 12: value = (long)process.MemoryManager.GetAddrSpaceBaseAddr(); break;
+
+ case 13: value = (long)process.MemoryManager.GetAddrSpaceSize(); break;
+
+ case 14: value = (long)process.MemoryManager.StackRegionStart; break;
+ case 15: value = (long)(process.MemoryManager.StackRegionEnd -
+ process.MemoryManager.StackRegionStart); break;
+
+ case 16: value = (long)process.PersonalMmHeapPagesCount * KMemoryManager.PageSize; break;
+
+ case 17:
+ if (process.PersonalMmHeapPagesCount != 0)
+ {
+ value = process.MemoryManager.GetMmUsedPages() * KMemoryManager.PageSize;
+ }
+
+ break;
+
+ case 18: value = process.TitleId; break;
+
+ case 20: value = (long)process.UserExceptionContextAddress; break;
+
+ case 21: value = (long)process.GetMemoryCapacityWithoutPersonalMmHeap(); break;
+
+ case 22: value = (long)process.GetMemoryUsageWithoutPersonalMmHeap(); break;
+ }
+
+ break;
+ }
+
+ case 8:
+ {
+ if (handle != 0)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ if (subId != 0)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ value = _system.Scheduler.GetCurrentProcess().Debug ? 1 : 0;
+
+ break;
+ }
+
+ case 9:
+ {
+ if (handle != 0)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ if (subId != 0)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ if (currentProcess.ResourceLimit != null)
+ {
+ KHandleTable handleTable = currentProcess.HandleTable;
+ KResourceLimit resourceLimit = currentProcess.ResourceLimit;
+
+ KernelResult result = handleTable.GenerateHandle(resourceLimit, out int resLimHandle);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ value = (uint)resLimHandle;
+ }
+
+ break;
+ }
+
+ case 10:
+ {
+ if (handle != 0)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ int currentCore = _system.Scheduler.GetCurrentThread().CurrentCore;
+
+ if (subId != -1 && subId != currentCore)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ value = _system.Scheduler.CoreContexts[currentCore].TotalIdleTimeTicks;
+
+ break;
+ }
+
+ case 11:
+ {
+ if (handle != 0)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ if ((ulong)subId > 3)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+
+ value = currentProcess.RandomEntropy[subId];
+
+ break;
+ }
+
+ case 0xf0000002u:
+ {
+ if (subId < -1 || subId > 3)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ KThread thread = _system.Scheduler.GetCurrentProcess().HandleTable.GetKThread(handle);
+
+ if (thread == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ KThread currentThread = _system.Scheduler.GetCurrentThread();
+
+ int currentCore = currentThread.CurrentCore;
+
+ if (subId != -1 && subId != currentCore)
+ {
+ return KernelResult.Success;
+ }
+
+ KCoreContext coreContext = _system.Scheduler.CoreContexts[currentCore];
+
+ long timeDelta = PerformanceCounter.ElapsedMilliseconds - coreContext.LastContextSwitchTime;
+
+ if (subId != -1)
+ {
+ value = KTimeManager.ConvertMillisecondsToTicks(timeDelta);
+ }
+ else
+ {
+ long totalTimeRunning = thread.TotalTimeRunning;
+
+ if (thread == currentThread)
+ {
+ totalTimeRunning += timeDelta;
+ }
+
+ value = KTimeManager.ConvertMillisecondsToTicks(totalTimeRunning);
+ }
+
+ break;
+ }
+
+ default: return KernelResult.InvalidEnumValue;
+ }
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult CreateEvent64(out int wEventHandle, out int rEventHandle)
+ {
+ return CreateEvent(out wEventHandle, out rEventHandle);
+ }
+
+ private KernelResult CreateEvent(out int wEventHandle, out int rEventHandle)
+ {
+ KEvent Event = new KEvent(_system);
+
+ KernelResult result = _process.HandleTable.GenerateHandle(Event.WritableEvent, out wEventHandle);
+
+ if (result == KernelResult.Success)
+ {
+ result = _process.HandleTable.GenerateHandle(Event.ReadableEvent, out rEventHandle);
+
+ if (result != KernelResult.Success)
+ {
+ _process.HandleTable.CloseHandle(wEventHandle);
+ }
+ }
+ else
+ {
+ rEventHandle = 0;
+ }
+
+ return result;
+ }
+
+ public KernelResult GetProcessList64(ulong address, int maxCount, out int count)
+ {
+ return GetProcessList(address, maxCount, out count);
+ }
+
+ private KernelResult GetProcessList(ulong address, int maxCount, out int count)
+ {
+ count = 0;
+
+ if ((maxCount >> 28) != 0)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ if (maxCount != 0)
+ {
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ ulong copySize = (ulong)maxCount * 8;
+
+ if (address + copySize <= address)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (currentProcess.MemoryManager.OutsideAddrSpace(address, copySize))
+ {
+ return KernelResult.InvalidMemState;
+ }
+ }
+
+ int copyCount = 0;
+
+ lock (_system.Processes)
+ {
+ foreach (KProcess process in _system.Processes.Values)
+ {
+ if (copyCount < maxCount)
+ {
+ if (!KernelTransfer.KernelToUserInt64(_system, address + (ulong)copyCount * 8, process.Pid))
+ {
+ return KernelResult.UserCopyFailed;
+ }
+ }
+
+ copyCount++;
+ }
+ }
+
+ count = copyCount;
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult GetSystemInfo64(uint id, int handle, long subId, out long value)
+ {
+ return GetSystemInfo(id, handle, subId, out value);
+ }
+
+ private KernelResult GetSystemInfo(uint id, int handle, long subId, out long value)
+ {
+ value = 0;
+
+ if (id > 2)
+ {
+ return KernelResult.InvalidEnumValue;
+ }
+
+ if (handle != 0)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ if (id < 2)
+ {
+ if ((ulong)subId > 3)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ KMemoryRegionManager region = _system.MemoryRegions[subId];
+
+ switch (id)
+ {
+ //Memory region capacity.
+ case 0: value = (long)region.Size; break;
+
+ //Memory region free space.
+ case 1:
+ {
+ ulong freePagesCount = region.GetFreePages();
+
+ value = (long)(freePagesCount * KMemoryManager.PageSize);
+
+ break;
+ }
+ }
+ }
+ else /* if (Id == 2) */
+ {
+ if ((ulong)subId > 1)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ switch (subId)
+ {
+ case 0: value = _system.PrivilegedProcessLowestId; break;
+ case 1: value = _system.PrivilegedProcessHighestId; break;
+ }
+ }
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult CreatePort64(
+ int maxSessions,
+ bool isLight,
+ ulong namePtr,
+ out int serverPortHandle,
+ out int clientPortHandle)
+ {
+ return CreatePort(maxSessions, isLight, namePtr, out serverPortHandle, out clientPortHandle);
+ }
+
+ private KernelResult CreatePort(
+ int maxSessions,
+ bool isLight,
+ ulong namePtr,
+ out int serverPortHandle,
+ out int clientPortHandle)
+ {
+ serverPortHandle = clientPortHandle = 0;
+
+ if (maxSessions < 1)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ KPort port = new KPort(_system);
+
+ port.Initialize(maxSessions, isLight, (long)namePtr);
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ClientPort, out clientPortHandle);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out serverPortHandle);
+
+ if (result != KernelResult.Success)
+ {
+ currentProcess.HandleTable.CloseHandle(clientPortHandle);
+ }
+
+ return result;
+ }
+
+ public KernelResult ManageNamedPort64(ulong namePtr, int maxSessions, out int handle)
+ {
+ return ManageNamedPort(namePtr, maxSessions, out handle);
+ }
+
+ private KernelResult ManageNamedPort(ulong namePtr, int maxSessions, out int handle)
+ {
+ handle = 0;
+
+ if (!KernelTransfer.UserToKernelString(_system, namePtr, 12, out string name))
+ {
+ return KernelResult.UserCopyFailed;
+ }
+
+ if (maxSessions < 0 || name.Length > 11)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ if (maxSessions == 0)
+ {
+ return KClientPort.RemoveName(_system, name);
+ }
+
+ KPort port = new KPort(_system);
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out handle);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ port.Initialize(maxSessions, false, 0);
+
+ result = port.SetName(name);
+
+ if (result != KernelResult.Success)
+ {
+ currentProcess.HandleTable.CloseHandle(handle);
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs
new file mode 100644
index 00000000..a6111777
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs
@@ -0,0 +1,340 @@
+using ChocolArm64.State;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
+{
+ static class SvcTable
+ {
+ private const int SvcFuncMaxArguments = 8;
+
+ private static Dictionary<int, string> _svcFuncs64;
+
+ private static Action<SvcHandler, CpuThreadState>[] _svcTable64;
+
+ static SvcTable()
+ {
+ _svcFuncs64 = new Dictionary<int, string>
+ {
+ { 0x01, nameof(SvcHandler.SetHeapSize64) },
+ { 0x03, nameof(SvcHandler.SetMemoryAttribute64) },
+ { 0x04, nameof(SvcHandler.MapMemory64) },
+ { 0x05, nameof(SvcHandler.UnmapMemory64) },
+ { 0x06, nameof(SvcHandler.QueryMemory64) },
+ { 0x07, nameof(SvcHandler.ExitProcess64) },
+ { 0x08, nameof(SvcHandler.CreateThread64) },
+ { 0x09, nameof(SvcHandler.StartThread64) },
+ { 0x0a, nameof(SvcHandler.ExitThread64) },
+ { 0x0b, nameof(SvcHandler.SleepThread64) },
+ { 0x0c, nameof(SvcHandler.GetThreadPriority64) },
+ { 0x0d, nameof(SvcHandler.SetThreadPriority64) },
+ { 0x0e, nameof(SvcHandler.GetThreadCoreMask64) },
+ { 0x0f, nameof(SvcHandler.SetThreadCoreMask64) },
+ { 0x10, nameof(SvcHandler.GetCurrentProcessorNumber64) },
+ { 0x11, nameof(SvcHandler.SignalEvent64) },
+ { 0x12, nameof(SvcHandler.ClearEvent64) },
+ { 0x13, nameof(SvcHandler.MapSharedMemory64) },
+ { 0x14, nameof(SvcHandler.UnmapSharedMemory64) },
+ { 0x15, nameof(SvcHandler.CreateTransferMemory64) },
+ { 0x16, nameof(SvcHandler.CloseHandle64) },
+ { 0x17, nameof(SvcHandler.ResetSignal64) },
+ { 0x18, nameof(SvcHandler.WaitSynchronization64) },
+ { 0x19, nameof(SvcHandler.CancelSynchronization64) },
+ { 0x1a, nameof(SvcHandler.ArbitrateLock64) },
+ { 0x1b, nameof(SvcHandler.ArbitrateUnlock64) },
+ { 0x1c, nameof(SvcHandler.WaitProcessWideKeyAtomic64) },
+ { 0x1d, nameof(SvcHandler.SignalProcessWideKey64) },
+ { 0x1e, nameof(SvcHandler.GetSystemTick64) },
+ { 0x1f, nameof(SvcHandler.ConnectToNamedPort64) },
+ { 0x21, nameof(SvcHandler.SendSyncRequest64) },
+ { 0x22, nameof(SvcHandler.SendSyncRequestWithUserBuffer64) },
+ { 0x24, nameof(SvcHandler.GetProcessId64) },
+ { 0x25, nameof(SvcHandler.GetThreadId64) },
+ { 0x26, nameof(SvcHandler.Break64) },
+ { 0x27, nameof(SvcHandler.OutputDebugString64) },
+ { 0x29, nameof(SvcHandler.GetInfo64) },
+ { 0x2c, nameof(SvcHandler.MapPhysicalMemory64) },
+ { 0x2d, nameof(SvcHandler.UnmapPhysicalMemory64) },
+ { 0x32, nameof(SvcHandler.SetThreadActivity64) },
+ { 0x33, nameof(SvcHandler.GetThreadContext364) },
+ { 0x34, nameof(SvcHandler.WaitForAddress64) },
+ { 0x35, nameof(SvcHandler.SignalToAddress64) },
+ { 0x45, nameof(SvcHandler.CreateEvent64) },
+ { 0x65, nameof(SvcHandler.GetProcessList64) },
+ { 0x6f, nameof(SvcHandler.GetSystemInfo64) },
+ { 0x70, nameof(SvcHandler.CreatePort64) },
+ { 0x71, nameof(SvcHandler.ManageNamedPort64) }
+ };
+
+ _svcTable64 = new Action<SvcHandler, CpuThreadState>[0x80];
+ }
+
+ public static Action<SvcHandler, CpuThreadState> GetSvcFunc(int svcId)
+ {
+ if (_svcTable64[svcId] != null)
+ {
+ return _svcTable64[svcId];
+ }
+
+ if (_svcFuncs64.TryGetValue(svcId, out string svcName))
+ {
+ return _svcTable64[svcId] = GenerateMethod(svcName);
+ }
+
+ return null;
+ }
+
+ private static Action<SvcHandler, CpuThreadState> GenerateMethod(string svcName)
+ {
+ Type[] argTypes = new Type[] { typeof(SvcHandler), typeof(CpuThreadState) };
+
+ DynamicMethod method = new DynamicMethod(svcName, null, argTypes);
+
+ MethodInfo methodInfo = typeof(SvcHandler).GetMethod(svcName);
+
+ ParameterInfo[] methodArgs = methodInfo.GetParameters();
+
+ if (methodArgs.Length > SvcFuncMaxArguments)
+ {
+ throw new InvalidOperationException($"Method \"{svcName}\" has too many arguments, max is 8.");
+ }
+
+ ILGenerator generator = method.GetILGenerator();
+
+ void ConvertToArgType(Type sourceType)
+ {
+ CheckIfTypeIsSupported(sourceType, svcName);
+
+ switch (Type.GetTypeCode(sourceType))
+ {
+ case TypeCode.UInt32: generator.Emit(OpCodes.Conv_U4); break;
+ case TypeCode.Int32: generator.Emit(OpCodes.Conv_I4); break;
+ case TypeCode.UInt16: generator.Emit(OpCodes.Conv_U2); break;
+ case TypeCode.Int16: generator.Emit(OpCodes.Conv_I2); break;
+ case TypeCode.Byte: generator.Emit(OpCodes.Conv_U1); break;
+ case TypeCode.SByte: generator.Emit(OpCodes.Conv_I1); break;
+
+ case TypeCode.Boolean:
+ generator.Emit(OpCodes.Conv_I4);
+ generator.Emit(OpCodes.Ldc_I4_1);
+ generator.Emit(OpCodes.And);
+ break;
+ }
+ }
+
+ void ConvertToFieldType(Type sourceType)
+ {
+ CheckIfTypeIsSupported(sourceType, svcName);
+
+ switch (Type.GetTypeCode(sourceType))
+ {
+ case TypeCode.UInt32:
+ case TypeCode.Int32:
+ case TypeCode.UInt16:
+ case TypeCode.Int16:
+ case TypeCode.Byte:
+ case TypeCode.SByte:
+ case TypeCode.Boolean:
+ generator.Emit(OpCodes.Conv_U8);
+ break;
+ }
+ }
+
+ //For functions returning output values, the first registers
+ //are used to hold pointers where the value will be stored,
+ //so they can't be used to pass argument and we must
+ //skip them.
+ int byRefArgsCount = 0;
+
+ for (int index = 0; index < methodArgs.Length; index++)
+ {
+ if (methodArgs[index].ParameterType.IsByRef)
+ {
+ byRefArgsCount++;
+ }
+ }
+
+ //Print all the arguments for debugging purposes.
+ int inputArgsCount = methodArgs.Length - byRefArgsCount;
+
+ generator.Emit(OpCodes.Ldc_I4_S, inputArgsCount);
+
+ generator.Emit(OpCodes.Newarr, typeof(object));
+
+ string argsFormat = svcName;
+
+ for (int index = 0; index < inputArgsCount; index++)
+ {
+ argsFormat += $" {methodArgs[index].Name}: 0x{{{index}:X8}},";
+
+ generator.Emit(OpCodes.Dup);
+ generator.Emit(OpCodes.Ldc_I4_S, index);
+ generator.Emit(OpCodes.Conv_I);
+
+ generator.Emit(OpCodes.Ldarg_1);
+ generator.Emit(OpCodes.Ldfld, GetStateFieldX(byRefArgsCount + index));
+
+ generator.Emit(OpCodes.Box, typeof(ulong));
+
+ generator.Emit(OpCodes.Stelem_Ref);
+ }
+
+ argsFormat = argsFormat.Substring(0, argsFormat.Length - 1);
+
+ generator.Emit(OpCodes.Ldstr, argsFormat);
+
+ BindingFlags staticNonPublic = BindingFlags.NonPublic | BindingFlags.Static;
+
+ MethodInfo printArgsMethod = typeof(SvcTable).GetMethod(nameof(PrintArguments), staticNonPublic);
+
+ generator.Emit(OpCodes.Call, printArgsMethod);
+
+ //Call the SVC function handler.
+ generator.Emit(OpCodes.Ldarg_0);
+
+ List<LocalBuilder> locals = new List<LocalBuilder>();
+
+ for (int index = 0; index < methodArgs.Length; index++)
+ {
+ Type argType = methodArgs[index].ParameterType;
+
+ if (argType.IsByRef)
+ {
+ argType = argType.GetElementType();
+
+ LocalBuilder local = generator.DeclareLocal(argType);
+
+ locals.Add(local);
+
+ if (!methodArgs[index].IsOut)
+ {
+ throw new InvalidOperationException($"Method \"{svcName}\" has a invalid ref type \"{argType.Name}\".");
+ }
+
+ generator.Emit(OpCodes.Ldloca_S, (byte)local.LocalIndex);
+ }
+ else
+ {
+ generator.Emit(OpCodes.Ldarg_1);
+ generator.Emit(OpCodes.Ldfld, GetStateFieldX(byRefArgsCount + index));
+
+ ConvertToArgType(argType);
+ }
+ }
+
+ generator.Emit(OpCodes.Call, methodInfo);
+
+ int outRegIndex = 0;
+
+ Type retType = methodInfo.ReturnType;
+
+ //Print result code.
+ if (retType == typeof(KernelResult))
+ {
+ MethodInfo printResultMethod = typeof(SvcTable).GetMethod(nameof(PrintResult), staticNonPublic);
+
+ generator.Emit(OpCodes.Dup);
+ generator.Emit(OpCodes.Ldstr, svcName);
+ generator.Emit(OpCodes.Call, printResultMethod);
+ }
+
+ //Save return value into register X0 (when the method has a return value).
+ if (retType != typeof(void))
+ {
+ CheckIfTypeIsSupported(retType, svcName);
+
+ LocalBuilder tempLocal = generator.DeclareLocal(retType);
+
+ generator.Emit(OpCodes.Stloc, tempLocal);
+ generator.Emit(OpCodes.Ldarg_1);
+ generator.Emit(OpCodes.Ldloc, tempLocal);
+
+ ConvertToFieldType(retType);
+
+ generator.Emit(OpCodes.Stfld, GetStateFieldX(outRegIndex++));
+ }
+
+ for (int index = 0; index < locals.Count; index++)
+ {
+ generator.Emit(OpCodes.Ldarg_1);
+ generator.Emit(OpCodes.Ldloc, locals[index]);
+
+ ConvertToFieldType(locals[index].LocalType);
+
+ generator.Emit(OpCodes.Stfld, GetStateFieldX(outRegIndex++));
+ }
+
+ //Zero out the remaining unused registers.
+ while (outRegIndex < SvcFuncMaxArguments)
+ {
+ generator.Emit(OpCodes.Ldarg_1);
+ generator.Emit(OpCodes.Ldc_I8, 0L);
+ generator.Emit(OpCodes.Stfld, GetStateFieldX(outRegIndex++));
+ }
+
+ generator.Emit(OpCodes.Ret);
+
+ return (Action<SvcHandler, CpuThreadState>)method.CreateDelegate(typeof(Action<SvcHandler, CpuThreadState>));
+ }
+
+ private static FieldInfo GetStateFieldX(int index)
+ {
+ switch (index)
+ {
+ case 0: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X0));
+ case 1: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X1));
+ case 2: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X2));
+ case 3: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X3));
+ case 4: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X4));
+ case 5: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X5));
+ case 6: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X6));
+ case 7: return typeof(CpuThreadState).GetField(nameof(CpuThreadState.X7));
+ }
+
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ private static void CheckIfTypeIsSupported(Type type, string svcName)
+ {
+ switch (Type.GetTypeCode(type))
+ {
+ case TypeCode.UInt64:
+ case TypeCode.Int64:
+ case TypeCode.UInt32:
+ case TypeCode.Int32:
+ case TypeCode.UInt16:
+ case TypeCode.Int16:
+ case TypeCode.Byte:
+ case TypeCode.SByte:
+ case TypeCode.Boolean:
+ return;
+ }
+
+ throw new InvalidSvcException($"Method \"{svcName}\" has a invalid ref type \"{type.Name}\".");
+ }
+
+ private static void PrintResult(KernelResult result, string svcName)
+ {
+ if (result != KernelResult.Success &&
+ result != KernelResult.TimedOut &&
+ result != KernelResult.Cancelled &&
+ result != KernelResult.InvalidState)
+ {
+ Logger.PrintWarning(LogClass.KernelSvc, $"{svcName} returned error {result}.");
+ }
+ else
+ {
+ Logger.PrintDebug(LogClass.KernelSvc, $"{svcName} returned result {result}.");
+ }
+ }
+
+ private static void PrintArguments(object[] argValues, string format)
+ {
+ Logger.PrintDebug(LogClass.KernelSvc, string.Format(format, argValues));
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs
new file mode 100644
index 00000000..1e1927fe
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs
@@ -0,0 +1,420 @@
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
+{
+ partial class SvcHandler
+ {
+ public KernelResult CreateThread64(
+ ulong entrypoint,
+ ulong argsPtr,
+ ulong stackTop,
+ int priority,
+ int cpuCore,
+ out int handle)
+ {
+ return CreateThread(entrypoint, argsPtr, stackTop, priority, cpuCore, out handle);
+ }
+
+ private KernelResult CreateThread(
+ ulong entrypoint,
+ ulong argsPtr,
+ ulong stackTop,
+ int priority,
+ int cpuCore,
+ out int handle)
+ {
+ handle = 0;
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ if (cpuCore == -2)
+ {
+ cpuCore = currentProcess.DefaultCpuCore;
+ }
+
+ if ((uint)cpuCore >= KScheduler.CpuCoresCount || !currentProcess.IsCpuCoreAllowed(cpuCore))
+ {
+ return KernelResult.InvalidCpuCore;
+ }
+
+ if ((uint)priority >= KScheduler.PrioritiesCount || !currentProcess.IsPriorityAllowed(priority))
+ {
+ return KernelResult.InvalidPriority;
+ }
+
+ long timeout = KTimeManager.ConvertMillisecondsToNanoseconds(100);
+
+ if (currentProcess.ResourceLimit != null &&
+ !currentProcess.ResourceLimit.Reserve(LimitableResource.Thread, 1, timeout))
+ {
+ return KernelResult.ResLimitExceeded;
+ }
+
+ KThread thread = new KThread(_system);
+
+ KernelResult result = currentProcess.InitializeThread(
+ thread,
+ entrypoint,
+ argsPtr,
+ stackTop,
+ priority,
+ cpuCore);
+
+ if (result != KernelResult.Success)
+ {
+ currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
+
+ return result;
+ }
+
+ result = _process.HandleTable.GenerateHandle(thread, out handle);
+
+ if (result != KernelResult.Success)
+ {
+ thread.Terminate();
+
+ currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
+ }
+
+ return result;
+ }
+
+ public KernelResult StartThread64(int handle)
+ {
+ return StartThread(handle);
+ }
+
+ private KernelResult StartThread(int handle)
+ {
+ KThread thread = _process.HandleTable.GetObject<KThread>(handle);
+
+ if (thread != null)
+ {
+ return thread.Start();
+ }
+ else
+ {
+ return KernelResult.InvalidHandle;
+ }
+ }
+
+ public void ExitThread64()
+ {
+ ExitThread();
+ }
+
+ private void ExitThread()
+ {
+ KThread currentThread = _system.Scheduler.GetCurrentThread();
+
+ _system.Scheduler.ExitThread(currentThread);
+
+ currentThread.Exit();
+ }
+
+ public void SleepThread64(long timeout)
+ {
+ SleepThread(timeout);
+ }
+
+ private void SleepThread(long timeout)
+ {
+ KThread currentThread = _system.Scheduler.GetCurrentThread();
+
+ if (timeout < 1)
+ {
+ switch (timeout)
+ {
+ case 0: currentThread.Yield(); break;
+ case -1: currentThread.YieldWithLoadBalancing(); break;
+ case -2: currentThread.YieldAndWaitForLoadBalancing(); break;
+ }
+ }
+ else
+ {
+ currentThread.Sleep(timeout);
+ }
+ }
+
+ public KernelResult GetThreadPriority64(int handle, out int priority)
+ {
+ return GetThreadPriority(handle, out priority);
+ }
+
+ private KernelResult GetThreadPriority(int handle, out int priority)
+ {
+ KThread thread = _process.HandleTable.GetKThread(handle);
+
+ if (thread != null)
+ {
+ priority = thread.DynamicPriority;
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ priority = 0;
+
+ return KernelResult.InvalidHandle;
+ }
+ }
+
+ public KernelResult SetThreadPriority64(int handle, int priority)
+ {
+ return SetThreadPriority(handle, priority);
+ }
+
+ public KernelResult SetThreadPriority(int handle, int priority)
+ {
+ //TODO: NPDM check.
+
+ KThread thread = _process.HandleTable.GetKThread(handle);
+
+ if (thread == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ thread.SetPriority(priority);
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult GetThreadCoreMask64(int handle, out int preferredCore, out long affinityMask)
+ {
+ return GetThreadCoreMask(handle, out preferredCore, out affinityMask);
+ }
+
+ private KernelResult GetThreadCoreMask(int handle, out int preferredCore, out long affinityMask)
+ {
+ KThread thread = _process.HandleTable.GetKThread(handle);
+
+ if (thread != null)
+ {
+ preferredCore = thread.PreferredCore;
+ affinityMask = thread.AffinityMask;
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ preferredCore = 0;
+ affinityMask = 0;
+
+ return KernelResult.InvalidHandle;
+ }
+ }
+
+ public KernelResult SetThreadCoreMask64(int handle, int preferredCore, long affinityMask)
+ {
+ return SetThreadCoreMask(handle, preferredCore, affinityMask);
+ }
+
+ private KernelResult SetThreadCoreMask(int handle, int preferredCore, long affinityMask)
+ {
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ if (preferredCore == -2)
+ {
+ preferredCore = currentProcess.DefaultCpuCore;
+
+ affinityMask = 1 << preferredCore;
+ }
+ else
+ {
+ if ((currentProcess.Capabilities.AllowedCpuCoresMask | affinityMask) !=
+ currentProcess.Capabilities.AllowedCpuCoresMask)
+ {
+ return KernelResult.InvalidCpuCore;
+ }
+
+ if (affinityMask == 0)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ if ((uint)preferredCore > 3)
+ {
+ if ((preferredCore | 2) != -1)
+ {
+ return KernelResult.InvalidCpuCore;
+ }
+ }
+ else if ((affinityMask & (1 << preferredCore)) == 0)
+ {
+ return KernelResult.InvalidCombination;
+ }
+ }
+
+ KThread thread = _process.HandleTable.GetKThread(handle);
+
+ if (thread == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ return thread.SetCoreAndAffinityMask(preferredCore, affinityMask);
+ }
+
+ public int GetCurrentProcessorNumber64()
+ {
+ return _system.Scheduler.GetCurrentThread().CurrentCore;
+ }
+
+ public KernelResult GetThreadId64(int handle, out long threadUid)
+ {
+ return GetThreadId(handle, out threadUid);
+ }
+
+ private KernelResult GetThreadId(int handle, out long threadUid)
+ {
+ KThread thread = _process.HandleTable.GetKThread(handle);
+
+ if (thread != null)
+ {
+ threadUid = thread.ThreadUid;
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ threadUid = 0;
+
+ return KernelResult.InvalidHandle;
+ }
+ }
+
+ public KernelResult SetThreadActivity64(int handle, bool pause)
+ {
+ return SetThreadActivity(handle, pause);
+ }
+
+ private KernelResult SetThreadActivity(int handle, bool pause)
+ {
+ KThread thread = _process.HandleTable.GetObject<KThread>(handle);
+
+ if (thread == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ if (thread.Owner != _system.Scheduler.GetCurrentProcess())
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ if (thread == _system.Scheduler.GetCurrentThread())
+ {
+ return KernelResult.InvalidThread;
+ }
+
+ return thread.SetActivity(pause);
+ }
+
+ public KernelResult GetThreadContext364(ulong address, int handle)
+ {
+ return GetThreadContext3(address, handle);
+ }
+
+ private KernelResult GetThreadContext3(ulong address, int handle)
+ {
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+ KThread currentThread = _system.Scheduler.GetCurrentThread();
+
+ KThread thread = _process.HandleTable.GetObject<KThread>(handle);
+
+ if (thread == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ if (thread.Owner != currentProcess)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ if (currentThread == thread)
+ {
+ return KernelResult.InvalidThread;
+ }
+
+ _memory.WriteUInt64((long)address + 0x0, thread.Context.ThreadState.X0);
+ _memory.WriteUInt64((long)address + 0x8, thread.Context.ThreadState.X1);
+ _memory.WriteUInt64((long)address + 0x10, thread.Context.ThreadState.X2);
+ _memory.WriteUInt64((long)address + 0x18, thread.Context.ThreadState.X3);
+ _memory.WriteUInt64((long)address + 0x20, thread.Context.ThreadState.X4);
+ _memory.WriteUInt64((long)address + 0x28, thread.Context.ThreadState.X5);
+ _memory.WriteUInt64((long)address + 0x30, thread.Context.ThreadState.X6);
+ _memory.WriteUInt64((long)address + 0x38, thread.Context.ThreadState.X7);
+ _memory.WriteUInt64((long)address + 0x40, thread.Context.ThreadState.X8);
+ _memory.WriteUInt64((long)address + 0x48, thread.Context.ThreadState.X9);
+ _memory.WriteUInt64((long)address + 0x50, thread.Context.ThreadState.X10);
+ _memory.WriteUInt64((long)address + 0x58, thread.Context.ThreadState.X11);
+ _memory.WriteUInt64((long)address + 0x60, thread.Context.ThreadState.X12);
+ _memory.WriteUInt64((long)address + 0x68, thread.Context.ThreadState.X13);
+ _memory.WriteUInt64((long)address + 0x70, thread.Context.ThreadState.X14);
+ _memory.WriteUInt64((long)address + 0x78, thread.Context.ThreadState.X15);
+ _memory.WriteUInt64((long)address + 0x80, thread.Context.ThreadState.X16);
+ _memory.WriteUInt64((long)address + 0x88, thread.Context.ThreadState.X17);
+ _memory.WriteUInt64((long)address + 0x90, thread.Context.ThreadState.X18);
+ _memory.WriteUInt64((long)address + 0x98, thread.Context.ThreadState.X19);
+ _memory.WriteUInt64((long)address + 0xa0, thread.Context.ThreadState.X20);
+ _memory.WriteUInt64((long)address + 0xa8, thread.Context.ThreadState.X21);
+ _memory.WriteUInt64((long)address + 0xb0, thread.Context.ThreadState.X22);
+ _memory.WriteUInt64((long)address + 0xb8, thread.Context.ThreadState.X23);
+ _memory.WriteUInt64((long)address + 0xc0, thread.Context.ThreadState.X24);
+ _memory.WriteUInt64((long)address + 0xc8, thread.Context.ThreadState.X25);
+ _memory.WriteUInt64((long)address + 0xd0, thread.Context.ThreadState.X26);
+ _memory.WriteUInt64((long)address + 0xd8, thread.Context.ThreadState.X27);
+ _memory.WriteUInt64((long)address + 0xe0, thread.Context.ThreadState.X28);
+ _memory.WriteUInt64((long)address + 0xe8, thread.Context.ThreadState.X29);
+ _memory.WriteUInt64((long)address + 0xf0, thread.Context.ThreadState.X30);
+ _memory.WriteUInt64((long)address + 0xf8, thread.Context.ThreadState.X31);
+
+ _memory.WriteInt64((long)address + 0x100, thread.LastPc);
+
+ _memory.WriteUInt64((long)address + 0x108, (ulong)thread.Context.ThreadState.Psr);
+
+ _memory.WriteVector128((long)address + 0x110, thread.Context.ThreadState.V0);
+ _memory.WriteVector128((long)address + 0x120, thread.Context.ThreadState.V1);
+ _memory.WriteVector128((long)address + 0x130, thread.Context.ThreadState.V2);
+ _memory.WriteVector128((long)address + 0x140, thread.Context.ThreadState.V3);
+ _memory.WriteVector128((long)address + 0x150, thread.Context.ThreadState.V4);
+ _memory.WriteVector128((long)address + 0x160, thread.Context.ThreadState.V5);
+ _memory.WriteVector128((long)address + 0x170, thread.Context.ThreadState.V6);
+ _memory.WriteVector128((long)address + 0x180, thread.Context.ThreadState.V7);
+ _memory.WriteVector128((long)address + 0x190, thread.Context.ThreadState.V8);
+ _memory.WriteVector128((long)address + 0x1a0, thread.Context.ThreadState.V9);
+ _memory.WriteVector128((long)address + 0x1b0, thread.Context.ThreadState.V10);
+ _memory.WriteVector128((long)address + 0x1c0, thread.Context.ThreadState.V11);
+ _memory.WriteVector128((long)address + 0x1d0, thread.Context.ThreadState.V12);
+ _memory.WriteVector128((long)address + 0x1e0, thread.Context.ThreadState.V13);
+ _memory.WriteVector128((long)address + 0x1f0, thread.Context.ThreadState.V14);
+ _memory.WriteVector128((long)address + 0x200, thread.Context.ThreadState.V15);
+ _memory.WriteVector128((long)address + 0x210, thread.Context.ThreadState.V16);
+ _memory.WriteVector128((long)address + 0x220, thread.Context.ThreadState.V17);
+ _memory.WriteVector128((long)address + 0x230, thread.Context.ThreadState.V18);
+ _memory.WriteVector128((long)address + 0x240, thread.Context.ThreadState.V19);
+ _memory.WriteVector128((long)address + 0x250, thread.Context.ThreadState.V20);
+ _memory.WriteVector128((long)address + 0x260, thread.Context.ThreadState.V21);
+ _memory.WriteVector128((long)address + 0x270, thread.Context.ThreadState.V22);
+ _memory.WriteVector128((long)address + 0x280, thread.Context.ThreadState.V23);
+ _memory.WriteVector128((long)address + 0x290, thread.Context.ThreadState.V24);
+ _memory.WriteVector128((long)address + 0x2a0, thread.Context.ThreadState.V25);
+ _memory.WriteVector128((long)address + 0x2b0, thread.Context.ThreadState.V26);
+ _memory.WriteVector128((long)address + 0x2c0, thread.Context.ThreadState.V27);
+ _memory.WriteVector128((long)address + 0x2d0, thread.Context.ThreadState.V28);
+ _memory.WriteVector128((long)address + 0x2e0, thread.Context.ThreadState.V29);
+ _memory.WriteVector128((long)address + 0x2f0, thread.Context.ThreadState.V30);
+ _memory.WriteVector128((long)address + 0x300, thread.Context.ThreadState.V31);
+
+ _memory.WriteInt32((long)address + 0x310, thread.Context.ThreadState.Fpcr);
+ _memory.WriteInt32((long)address + 0x314, thread.Context.ThreadState.Fpsr);
+ _memory.WriteInt64((long)address + 0x318, thread.Context.ThreadState.Tpidr);
+
+ return KernelResult.Success;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThreadSync.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThreadSync.cs
new file mode 100644
index 00000000..ecda9e2d
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThreadSync.cs
@@ -0,0 +1,250 @@
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
+{
+ partial class SvcHandler
+ {
+ public KernelResult WaitSynchronization64(ulong handlesPtr, int handlesCount, long timeout, out int handleIndex)
+ {
+ return WaitSynchronization(handlesPtr, handlesCount, timeout, out handleIndex);
+ }
+
+ private KernelResult WaitSynchronization(ulong handlesPtr, int handlesCount, long timeout, out int handleIndex)
+ {
+ handleIndex = 0;
+
+ if ((uint)handlesCount > 0x40)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ List<KSynchronizationObject> syncObjs = new List<KSynchronizationObject>();
+
+ for (int index = 0; index < handlesCount; index++)
+ {
+ int handle = _memory.ReadInt32((long)handlesPtr + index * 4);
+
+ KSynchronizationObject syncObj = _process.HandleTable.GetObject<KSynchronizationObject>(handle);
+
+ if (syncObj == null)
+ {
+ break;
+ }
+
+ syncObjs.Add(syncObj);
+ }
+
+ return _system.Synchronization.WaitFor(syncObjs.ToArray(), timeout, out handleIndex);
+ }
+
+ public KernelResult CancelSynchronization64(int handle)
+ {
+ return CancelSynchronization(handle);
+ }
+
+ private KernelResult CancelSynchronization(int handle)
+ {
+ KThread thread = _process.HandleTable.GetKThread(handle);
+
+ if (thread == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ thread.CancelSynchronization();
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult ArbitrateLock64(int ownerHandle, ulong mutexAddress, int requesterHandle)
+ {
+ return ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
+ }
+
+ private KernelResult ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle)
+ {
+ if (IsPointingInsideKernel(mutexAddress))
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (IsAddressNotWordAligned(mutexAddress))
+ {
+ return KernelResult.InvalidAddress;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ return currentProcess.AddressArbiter.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
+ }
+
+ public KernelResult ArbitrateUnlock64(ulong mutexAddress)
+ {
+ return ArbitrateUnlock(mutexAddress);
+ }
+
+ private KernelResult ArbitrateUnlock(ulong mutexAddress)
+ {
+ if (IsPointingInsideKernel(mutexAddress))
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (IsAddressNotWordAligned(mutexAddress))
+ {
+ return KernelResult.InvalidAddress;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ return currentProcess.AddressArbiter.ArbitrateUnlock(mutexAddress);
+ }
+
+ public KernelResult WaitProcessWideKeyAtomic64(
+ ulong mutexAddress,
+ ulong condVarAddress,
+ int handle,
+ long timeout)
+ {
+ return WaitProcessWideKeyAtomic(mutexAddress, condVarAddress, handle, timeout);
+ }
+
+ private KernelResult WaitProcessWideKeyAtomic(
+ ulong mutexAddress,
+ ulong condVarAddress,
+ int handle,
+ long timeout)
+ {
+ if (IsPointingInsideKernel(mutexAddress))
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (IsAddressNotWordAligned(mutexAddress))
+ {
+ return KernelResult.InvalidAddress;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ return currentProcess.AddressArbiter.WaitProcessWideKeyAtomic(
+ mutexAddress,
+ condVarAddress,
+ handle,
+ timeout);
+ }
+
+ public KernelResult SignalProcessWideKey64(ulong address, int count)
+ {
+ return SignalProcessWideKey(address, count);
+ }
+
+ private KernelResult SignalProcessWideKey(ulong address, int count)
+ {
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ currentProcess.AddressArbiter.SignalProcessWideKey(address, count);
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult WaitForAddress64(ulong address, ArbitrationType type, int value, long timeout)
+ {
+ return WaitForAddress(address, type, value, timeout);
+ }
+
+ private KernelResult WaitForAddress(ulong address, ArbitrationType type, int value, long timeout)
+ {
+ if (IsPointingInsideKernel(address))
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (IsAddressNotWordAligned(address))
+ {
+ return KernelResult.InvalidAddress;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KernelResult result;
+
+ switch (type)
+ {
+ case ArbitrationType.WaitIfLessThan:
+ result = currentProcess.AddressArbiter.WaitForAddressIfLessThan(address, value, false, timeout);
+ break;
+
+ case ArbitrationType.DecrementAndWaitIfLessThan:
+ result = currentProcess.AddressArbiter.WaitForAddressIfLessThan(address, value, true, timeout);
+ break;
+
+ case ArbitrationType.WaitIfEqual:
+ result = currentProcess.AddressArbiter.WaitForAddressIfEqual(address, value, timeout);
+ break;
+
+ default:
+ result = KernelResult.InvalidEnumValue;
+ break;
+ }
+
+ return result;
+ }
+
+ public KernelResult SignalToAddress64(ulong address, SignalType type, int value, int count)
+ {
+ return SignalToAddress(address, type, value, count);
+ }
+
+ private KernelResult SignalToAddress(ulong address, SignalType type, int value, int count)
+ {
+ if (IsPointingInsideKernel(address))
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (IsAddressNotWordAligned(address))
+ {
+ return KernelResult.InvalidAddress;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KernelResult result;
+
+ switch (type)
+ {
+ case SignalType.Signal:
+ result = currentProcess.AddressArbiter.Signal(address, count);
+ break;
+
+ case SignalType.SignalAndIncrementIfEqual:
+ result = currentProcess.AddressArbiter.SignalAndIncrementIfEqual(address, value, count);
+ break;
+
+ case SignalType.SignalAndModifyIfEqual:
+ result = currentProcess.AddressArbiter.SignalAndModifyIfEqual(address, value, count);
+ break;
+
+ default:
+ result = KernelResult.InvalidEnumValue;
+ break;
+ }
+
+ return result;
+ }
+
+ private bool IsPointingInsideKernel(ulong address)
+ {
+ return (address + 0x1000000000) < 0xffffff000;
+ }
+
+ private bool IsAddressNotWordAligned(ulong address)
+ {
+ return (address & 3) != 0;
+ }
+ }
+} \ No newline at end of file