diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2018-12-18 03:33:36 -0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-12-18 03:33:36 -0200 |
| commit | 0039bb639493b2d1e2764cae380311ba8e87704b (patch) | |
| tree | 63a912a95c8261775c2acb8a5b9ca0f10ad4ae33 /Ryujinx.HLE/HOS/Kernel/SupervisorCall | |
| parent | 2534a7f10c627810e6e0272b4cc9758e90f733c1 (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.cs | 9 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs | 61 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs | 394 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs | 762 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs | 340 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs | 420 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThreadSync.cs | 250 |
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 |
