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/SvcSystem.cs | |
| 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/SvcSystem.cs')
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs | 762 |
1 files changed, 762 insertions, 0 deletions
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; + } + } +} |
