diff options
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs')
-rw-r--r-- | Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs | 273 |
1 files changed, 184 insertions, 89 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index c1e7026b..5e32ca58 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -7,124 +7,223 @@ 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.Memory; using System; +using System.Threading; namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { class Syscall { - private readonly Switch _device; private readonly KernelContext _context; - public Syscall(Switch device, KernelContext context) + public Syscall(KernelContext context) { - _device = device; _context = context; } - // IPC + // Process - public KernelResult ConnectToNamedPort(ulong namePtr, out int handle) + public KernelResult GetProcessId(int handle, out long pid) + { + KProcess currentProcess = _context.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 KernelResult CreateProcess( + ProcessCreationInfo info, + ReadOnlySpan<int> capabilities, + out int handle, + IProcessContextFactory contextFactory, + ThreadStart customThreadStart = null) { handle = 0; - if (!KernelTransfer.UserToKernelString(_context, namePtr, 12, out string name)) + if ((info.Flags & ~ProcessCreationFlags.All) != 0) { - return KernelResult.UserCopyFailed; + return KernelResult.InvalidEnumValue; } - if (name.Length > 11) + // TODO: Address space check. + + if ((info.Flags & ProcessCreationFlags.PoolPartitionMask) > ProcessCreationFlags.PoolPartitionSystemNonSecure) { - return KernelResult.MaximumExceeded; + return KernelResult.InvalidEnumValue; } - KAutoObject autoObj = KAutoObject.FindNamedObject(_context, name); + if ((info.CodeAddress & 0x1fffff) != 0) + { + return KernelResult.InvalidAddress; + } - if (!(autoObj is KClientPort clientPort)) + if (info.CodePagesCount < 0 || info.SystemResourcePagesCount < 0) { - return KernelResult.NotFound; + return KernelResult.InvalidSize; } - KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); + if (info.Flags.HasFlag(ProcessCreationFlags.OptimizeMemoryAllocation) && + !info.Flags.HasFlag(ProcessCreationFlags.IsApplication)) + { + return KernelResult.InvalidThread; + } - KernelResult result = currentProcess.HandleTable.ReserveHandle(out handle); + KHandleTable handleTable = _context.Scheduler.GetCurrentProcess().HandleTable; - if (result != KernelResult.Success) + KProcess process = new KProcess(_context); + + using var _ = new OnScopeExit(process.DecrementReferenceCount); + + KResourceLimit resourceLimit; + + if (info.ResourceLimitHandle != 0) { - return result; + resourceLimit = handleTable.GetObject<KResourceLimit>(info.ResourceLimitHandle); + + if (resourceLimit == null) + { + return KernelResult.InvalidHandle; + } + } + else + { + resourceLimit = _context.ResourceLimit; } - result = clientPort.Connect(out KClientSession clientSession); + MemoryRegion memRegion = (info.Flags & ProcessCreationFlags.PoolPartitionMask) switch + { + ProcessCreationFlags.PoolPartitionApplication => MemoryRegion.Application, + ProcessCreationFlags.PoolPartitionApplet => MemoryRegion.Applet, + ProcessCreationFlags.PoolPartitionSystem => MemoryRegion.Service, + ProcessCreationFlags.PoolPartitionSystemNonSecure => MemoryRegion.NvServices, + _ => MemoryRegion.NvServices + }; + + KernelResult result = process.Initialize( + info, + capabilities, + resourceLimit, + memRegion, + contextFactory, + customThreadStart); if (result != KernelResult.Success) { - currentProcess.HandleTable.CancelHandleReservation(handle); - return result; } - currentProcess.HandleTable.SetReservedHandleObj(handle, clientSession); - - clientSession.DecrementReferenceCount(); + _context.Processes.TryAdd(process.Pid, process); - return result; + return handleTable.GenerateHandle(process, out handle); } - public KernelResult SendSyncRequestHLE(int handle) + public KernelResult StartProcess(int handle, int priority, int cpuCore, ulong mainThreadStackSize) { - KProcess process = _context.Scheduler.GetCurrentProcess(); + KProcess process = _context.Scheduler.GetCurrentProcess().HandleTable.GetObject<KProcess>(handle); - KClientSession clientSession = process.HandleTable.GetObject<KClientSession>(handle); + if (process == null) + { + return KernelResult.InvalidHandle; + } - if (clientSession == null || clientSession.Service == null) + if ((uint)cpuCore >= KScheduler.CpuCoresCount || !process.IsCpuCoreAllowed(cpuCore)) { - return SendSyncRequest(handle); + return KernelResult.InvalidCpuCore; + } + + if ((uint)priority >= KScheduler.PrioritiesCount || !process.IsPriorityAllowed(priority)) + { + return KernelResult.InvalidPriority; + } + + process.DefaultCpuCore = cpuCore; + + KernelResult result = process.Start(priority, mainThreadStackSize); + + if (result != KernelResult.Success) + { + return result; } - return SendSyncRequestWithUserBufferHLE((ulong)_context.Scheduler.GetCurrentThread().Context.Tpidr, 0x100, handle); + process.IncrementReferenceCount(); + + return KernelResult.Success; } - public KernelResult SendSyncRequestWithUserBufferHLE(ulong messagePtr, ulong messageSize, int handle) + // IPC + + public KernelResult ConnectToNamedPort(ulong namePtr, out int handle) { - KProcess process = _context.Scheduler.GetCurrentProcess(); + handle = 0; - byte[] messageData = new byte[messageSize]; + if (!KernelTransfer.UserToKernelString(_context, namePtr, 12, out string name)) + { + return KernelResult.UserCopyFailed; + } - process.CpuMemory.Read(messagePtr, messageData); + return ConnectToNamedPort(name, out handle); + } - KClientSession clientSession = process.HandleTable.GetObject<KClientSession>(handle); + public KernelResult ConnectToNamedPort(string name, out int handle) + { + handle = 0; - if (clientSession == null || clientSession.Service == null) + if (name.Length > 11) { - return SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle); + return KernelResult.MaximumExceeded; } - if (clientSession != null) - { - _context.CriticalSection.Enter(); + KAutoObject autoObj = KAutoObject.FindNamedObject(_context, name); - KThread currentThread = _context.Scheduler.GetCurrentThread(); + if (!(autoObj is KClientPort clientPort)) + { + return KernelResult.NotFound; + } - currentThread.SignaledObj = null; - currentThread.ObjSyncResult = KernelResult.Success; + KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); - currentThread.Reschedule(ThreadSchedState.Paused); + KernelResult result = currentProcess.HandleTable.ReserveHandle(out handle); - clientSession.Service.Server.PushMessage(_device, currentThread, clientSession, messagePtr, messageSize); + if (result != KernelResult.Success) + { + return result; + } - _context.CriticalSection.Leave(); + result = clientPort.Connect(out KClientSession clientSession); - return currentThread.ObjSyncResult; - } - else + if (result != KernelResult.Success) { - Logger.Warning?.Print(LogClass.KernelSvc, $"Invalid session handle 0x{handle:x8}!"); + currentProcess.HandleTable.CancelHandleReservation(handle); - return KernelResult.InvalidHandle; + return result; } + + currentProcess.HandleTable.SetReservedHandleObj(handle, clientSession); + + clientSession.DecrementReferenceCount(); + + return result; } - private KernelResult SendSyncRequest(int handle) + public KernelResult SendSyncRequest(int handle) { KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); @@ -407,9 +506,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.UserCopyFailed; } - KSynchronizationObject[] syncObjs = new KSynchronizationObject[handlesCount]; + return ReplyAndReceive(handles, replyTargetHandle, timeout, out handleIndex); + } - for (int index = 0; index < handlesCount; index++) + public KernelResult ReplyAndReceive(ReadOnlySpan<int> handles, int replyTargetHandle, long timeout, out int handleIndex) + { + handleIndex = 0; + + KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); + + KSynchronizationObject[] syncObjs = new KSynchronizationObject[handles.Length]; + + for (int index = 0; index < handles.Length; index++) { KSynchronizationObject obj = currentProcess.HandleTable.GetObject<KSynchronizationObject>(handles[index]); @@ -601,7 +709,19 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.UserCopyFailed; } - if (maxSessions < 0 || name.Length > 11) + if (name.Length > 11) + { + return KernelResult.MaximumExceeded; + } + + return ManageNamedPort(name, maxSessions, out handle); + } + + public KernelResult ManageNamedPort(string name, int maxSessions, out int handle) + { + handle = 0; + + if (maxSessions < 0) { return KernelResult.MaximumExceeded; } @@ -826,7 +946,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.Success; } - public KernelResult MapSharedMemory(int handle, ulong address, ulong size, MemoryPermission permission) + public KernelResult MapSharedMemory(int handle, ulong address, ulong size, KMemoryPermission permission) { if (!PageAligned(address)) { @@ -843,7 +963,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidMemState; } - if ((permission | MemoryPermission.Write) != MemoryPermission.ReadAndWrite) + if ((permission | KMemoryPermission.Write) != KMemoryPermission.ReadAndWrite) { return KernelResult.InvalidPermission; } @@ -912,7 +1032,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall currentProcess); } - public KernelResult CreateTransferMemory(ulong address, ulong size, MemoryPermission permission, out int handle) + public KernelResult CreateTransferMemory(ulong address, ulong size, KMemoryPermission permission, out int handle) { handle = 0; @@ -931,7 +1051,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidMemState; } - if (permission > MemoryPermission.ReadAndWrite || permission == MemoryPermission.Write) + if (permission > KMemoryPermission.ReadAndWrite || permission == KMemoryPermission.Write) { return KernelResult.InvalidPermission; } @@ -1119,7 +1239,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return targetProcess.MemoryManager.UnmapProcessCodeMemory(dst, src, size); } - public KernelResult SetProcessMemoryPermission(int handle, ulong src, ulong size, MemoryPermission permission) + public KernelResult SetProcessMemoryPermission(int handle, ulong src, ulong size, KMemoryPermission permission) { if (!PageAligned(src)) { @@ -1131,10 +1251,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidSize; } - if (permission != MemoryPermission.None && - permission != MemoryPermission.Read && - permission != MemoryPermission.ReadAndWrite && - permission != MemoryPermission.ReadAndExecute) + if (permission != KMemoryPermission.None && + permission != KMemoryPermission.Read && + permission != KMemoryPermission.ReadAndWrite && + permission != KMemoryPermission.ReadAndExecute) { return KernelResult.InvalidPermission; } @@ -1282,31 +1402,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return _context.Scheduler.GetCurrentThread().Context.CntpctEl0; } - public KernelResult GetProcessId(int handle, out long pid) - { - KProcess currentProcess = _context.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 Break(ulong reason) { KThread currentThread = _context.Scheduler.GetCurrentThread(); @@ -1997,7 +2092,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidThread; } - MemoryManager memory = currentProcess.CpuMemory; + IVirtualMemoryManager memory = currentProcess.CpuMemory; memory.Write(address + 0x0, thread.Context.GetX(0)); memory.Write(address + 0x8, thread.Context.GetX(1)); |