IPC refactor part 2: Use ReplyAndReceive on HLE services and remove special handling from kernel (#1458)
* IPC refactor part 2: Use ReplyAndReceive on HLE services and remove special handling from kernel * Fix for applet transfer memory + some nits * Keep handles if possible to avoid server handle table exhaustion * Fix IPC ZeroFill bug * am: Correctly implement CreateManagedDisplayLayer and implement CreateManagedDisplaySeparableLayer CreateManagedDisplaySeparableLayer is requires since 10.x+ when appletResourceUserId != 0 * Make it exit properly * Make ServiceNotImplementedException show the full message again * Allow yielding execution to avoid starving other threads * Only wait if active * Merge IVirtualMemoryManager and IAddressSpaceManager * Fix Ro loading data from the wrong process Co-authored-by: Thog <me@thog.eu>
This commit is contained in:
parent
461c24092a
commit
cf6cd71488
115 changed files with 2356 additions and 1088 deletions
|
@ -7,21 +7,167 @@ 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;
|
||||
}
|
||||
|
||||
// Process
|
||||
|
||||
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 ((info.Flags & ~ProcessCreationFlags.All) != 0)
|
||||
{
|
||||
return KernelResult.InvalidEnumValue;
|
||||
}
|
||||
|
||||
// TODO: Address space check.
|
||||
|
||||
if ((info.Flags & ProcessCreationFlags.PoolPartitionMask) > ProcessCreationFlags.PoolPartitionSystemNonSecure)
|
||||
{
|
||||
return KernelResult.InvalidEnumValue;
|
||||
}
|
||||
|
||||
if ((info.CodeAddress & 0x1fffff) != 0)
|
||||
{
|
||||
return KernelResult.InvalidAddress;
|
||||
}
|
||||
|
||||
if (info.CodePagesCount < 0 || info.SystemResourcePagesCount < 0)
|
||||
{
|
||||
return KernelResult.InvalidSize;
|
||||
}
|
||||
|
||||
if (info.Flags.HasFlag(ProcessCreationFlags.OptimizeMemoryAllocation) &&
|
||||
!info.Flags.HasFlag(ProcessCreationFlags.IsApplication))
|
||||
{
|
||||
return KernelResult.InvalidThread;
|
||||
}
|
||||
|
||||
KHandleTable handleTable = _context.Scheduler.GetCurrentProcess().HandleTable;
|
||||
|
||||
KProcess process = new KProcess(_context);
|
||||
|
||||
using var _ = new OnScopeExit(process.DecrementReferenceCount);
|
||||
|
||||
KResourceLimit resourceLimit;
|
||||
|
||||
if (info.ResourceLimitHandle != 0)
|
||||
{
|
||||
resourceLimit = handleTable.GetObject<KResourceLimit>(info.ResourceLimitHandle);
|
||||
|
||||
if (resourceLimit == null)
|
||||
{
|
||||
return KernelResult.InvalidHandle;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resourceLimit = _context.ResourceLimit;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
_context.Processes.TryAdd(process.Pid, process);
|
||||
|
||||
return handleTable.GenerateHandle(process, out handle);
|
||||
}
|
||||
|
||||
public KernelResult StartProcess(int handle, int priority, int cpuCore, ulong mainThreadStackSize)
|
||||
{
|
||||
KProcess process = _context.Scheduler.GetCurrentProcess().HandleTable.GetObject<KProcess>(handle);
|
||||
|
||||
if (process == null)
|
||||
{
|
||||
return KernelResult.InvalidHandle;
|
||||
}
|
||||
|
||||
if ((uint)cpuCore >= KScheduler.CpuCoresCount || !process.IsCpuCoreAllowed(cpuCore))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
process.IncrementReferenceCount();
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
// IPC
|
||||
|
||||
public KernelResult ConnectToNamedPort(ulong namePtr, out int handle)
|
||||
|
@ -33,6 +179,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
return KernelResult.UserCopyFailed;
|
||||
}
|
||||
|
||||
return ConnectToNamedPort(name, out handle);
|
||||
}
|
||||
|
||||
public KernelResult ConnectToNamedPort(string name, out int handle)
|
||||
{
|
||||
handle = 0;
|
||||
|
||||
if (name.Length > 11)
|
||||
{
|
||||
return KernelResult.MaximumExceeded;
|
||||
|
@ -70,61 +223,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
return result;
|
||||
}
|
||||
|
||||
public KernelResult SendSyncRequestHLE(int handle)
|
||||
{
|
||||
KProcess process = _context.Scheduler.GetCurrentProcess();
|
||||
|
||||
KClientSession clientSession = process.HandleTable.GetObject<KClientSession>(handle);
|
||||
|
||||
if (clientSession == null || clientSession.Service == null)
|
||||
{
|
||||
return SendSyncRequest(handle);
|
||||
}
|
||||
|
||||
return SendSyncRequestWithUserBufferHLE((ulong)_context.Scheduler.GetCurrentThread().Context.Tpidr, 0x100, handle);
|
||||
}
|
||||
|
||||
public KernelResult SendSyncRequestWithUserBufferHLE(ulong messagePtr, ulong messageSize, int handle)
|
||||
{
|
||||
KProcess process = _context.Scheduler.GetCurrentProcess();
|
||||
|
||||
byte[] messageData = new byte[messageSize];
|
||||
|
||||
process.CpuMemory.Read(messagePtr, messageData);
|
||||
|
||||
KClientSession clientSession = process.HandleTable.GetObject<KClientSession>(handle);
|
||||
|
||||
if (clientSession == null || clientSession.Service == null)
|
||||
{
|
||||
return SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle);
|
||||
}
|
||||
|
||||
if (clientSession != null)
|
||||
{
|
||||
_context.CriticalSection.Enter();
|
||||
|
||||
KThread currentThread = _context.Scheduler.GetCurrentThread();
|
||||
|
||||
currentThread.SignaledObj = null;
|
||||
currentThread.ObjSyncResult = KernelResult.Success;
|
||||
|
||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
||||
|
||||
clientSession.Service.Server.PushMessage(_device, currentThread, clientSession, messagePtr, messageSize);
|
||||
|
||||
_context.CriticalSection.Leave();
|
||||
|
||||
return currentThread.ObjSyncResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.KernelSvc, $"Invalid session handle 0x{handle:x8}!");
|
||||
|
||||
return KernelResult.InvalidHandle;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue