Improve kernel IPC related syscalls (#1379)
* Implement session count decrement when the handle is closed * Remove unused field * Implement SendSyncRequestWithUserBuffer, SendAsyncRequestWithUserBuffer and ReplyAndReceiveWithUserBuffer syscalls * Nits * Fix swapped copy dst/src * Add missing pointer buffer descriptor write on reply * Fix IPC unaligned buffer copy and restoring client attributes on reply * Oops * Fix SetIpcMappingPermission * Fix unaligned copy bugs * Free memory used for temporary IPC buffers
This commit is contained in:
parent
46f8cef6a9
commit
9f6b24edfd
14 changed files with 705 additions and 247 deletions
|
@ -115,19 +115,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
|
||||
ulong clientEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KMemoryManager.PageSize);
|
||||
ulong clientEndAddrRounded = BitUtils.AlignUp (clientEndAddr, KMemoryManager.PageSize);
|
||||
ulong serverEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KMemoryManager.PageSize);
|
||||
ulong serverEndAddrTruncated = BitUtils.AlignDown(serverEndAddr, KMemoryManager.PageSize);
|
||||
|
||||
if (clientEndAddrTruncated < clientAddrRounded)
|
||||
if (clientEndAddrTruncated < clientEndAddrRounded &&
|
||||
(clientAddrTruncated == clientAddrRounded || clientAddrTruncated < clientEndAddrTruncated))
|
||||
{
|
||||
KernelResult result = memoryManager.CopyDataToCurrentProcess(
|
||||
KernelResult result = memoryManager.CopyDataFromCurrentProcess(
|
||||
clientEndAddrTruncated,
|
||||
clientEndAddr - clientEndAddrTruncated,
|
||||
serverEndAddrTruncated,
|
||||
stateMask,
|
||||
stateMask,
|
||||
MemoryPermission.ReadAndWrite,
|
||||
attributeMask,
|
||||
MemoryAttribute.None);
|
||||
MemoryAttribute.None,
|
||||
serverEndAddrTruncated);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
{
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.HLE.HOS.Services;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
{
|
||||
class KClientPort : KSynchronizationObject
|
||||
{
|
||||
private int _sessionsCount;
|
||||
private int _currentCapacity;
|
||||
private readonly int _maxSessions;
|
||||
|
||||
private readonly KPort _parent;
|
||||
|
||||
public bool IsLight => _parent.IsLight;
|
||||
|
||||
private readonly object _countIncLock;
|
||||
|
||||
// TODO: Remove that, we need it for now to allow HLE
|
||||
// SM implementation to work with the new IPC system.
|
||||
public IpcService Service { get; set; }
|
||||
|
@ -24,8 +22,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
{
|
||||
_maxSessions = maxSessions;
|
||||
_parent = parent;
|
||||
|
||||
_countIncLock = new object();
|
||||
}
|
||||
|
||||
public KernelResult Connect(out KClientSession clientSession)
|
||||
|
@ -40,26 +36,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
return KernelResult.ResLimitExceeded;
|
||||
}
|
||||
|
||||
lock (_countIncLock)
|
||||
if (!IncrementSessionsCount())
|
||||
{
|
||||
if (_sessionsCount < _maxSessions)
|
||||
{
|
||||
_sessionsCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
|
||||
currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
|
||||
|
||||
return KernelResult.SessionCountExceeded;
|
||||
}
|
||||
|
||||
if (_currentCapacity < _sessionsCount)
|
||||
{
|
||||
_currentCapacity = _sessionsCount;
|
||||
}
|
||||
return KernelResult.SessionCountExceeded;
|
||||
}
|
||||
|
||||
KSession session = new KSession(KernelContext);
|
||||
KSession session = new KSession(KernelContext, this);
|
||||
|
||||
if (Service != null)
|
||||
{
|
||||
|
@ -93,18 +77,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
return KernelResult.ResLimitExceeded;
|
||||
}
|
||||
|
||||
lock (_countIncLock)
|
||||
if (!IncrementSessionsCount())
|
||||
{
|
||||
if (_sessionsCount < _maxSessions)
|
||||
{
|
||||
_sessionsCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
|
||||
currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
|
||||
|
||||
return KernelResult.SessionCountExceeded;
|
||||
}
|
||||
return KernelResult.SessionCountExceeded;
|
||||
}
|
||||
|
||||
KLightSession session = new KLightSession(KernelContext);
|
||||
|
@ -124,6 +101,43 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
return result;
|
||||
}
|
||||
|
||||
private bool IncrementSessionsCount()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int currentCount = _sessionsCount;
|
||||
|
||||
if (currentCount < _maxSessions)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref _sessionsCount, currentCount + 1, currentCount) == currentCount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
SignalIfMaximumReached(Interlocked.Decrement(ref _sessionsCount));
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
private void SignalIfMaximumReached(int value)
|
||||
{
|
||||
if (value == _maxSessions)
|
||||
{
|
||||
Signal();
|
||||
}
|
||||
}
|
||||
|
||||
public new static KernelResult RemoveName(KernelContext context, string name)
|
||||
{
|
||||
KAutoObject foundObj = FindNamedObject(context, name);
|
||||
|
|
|
@ -13,18 +13,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
|
||||
public ChannelState State { get; set; }
|
||||
|
||||
public KClientPort ParentPort { get; }
|
||||
|
||||
// TODO: Remove that, we need it for now to allow HLE
|
||||
// services implementation to work with the new IPC system.
|
||||
public IpcService Service { get; set; }
|
||||
|
||||
public KClientSession(KernelContext context, KSession parent) : base(context)
|
||||
public KClientSession(KernelContext context, KSession parent, KClientPort parentPort) : base(context)
|
||||
{
|
||||
_parent = parent;
|
||||
_parent = parent;
|
||||
ParentPort = parentPort;
|
||||
|
||||
parentPort?.IncrementReferenceCount();
|
||||
|
||||
State = ChannelState.Open;
|
||||
|
||||
CreatorProcess = context.Scheduler.GetCurrentProcess();
|
||||
|
||||
CreatorProcess.IncrementReferenceCount();
|
||||
}
|
||||
|
||||
|
@ -51,6 +55,30 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
return result;
|
||||
}
|
||||
|
||||
public KernelResult SendAsyncRequest(KWritableEvent asyncEvent, ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
|
||||
{
|
||||
KThread currentThread = KernelContext.Scheduler.GetCurrentThread();
|
||||
|
||||
KSessionRequest request = new KSessionRequest(currentThread, customCmdBuffAddr, customCmdBuffSize, asyncEvent);
|
||||
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
KernelResult result = _parent.ServerSession.EnqueueRequest(request);
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void DisconnectFromPort()
|
||||
{
|
||||
if (ParentPort != null)
|
||||
{
|
||||
ParentPort.Disconnect();
|
||||
ParentPort.DecrementReferenceCount();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Destroy()
|
||||
{
|
||||
_parent.DisconnectClient();
|
||||
|
|
|
@ -633,7 +633,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
{
|
||||
CloseAllHandles(clientMsg, serverHeader, clientProcess);
|
||||
|
||||
CancelRequest(request, clientResult);
|
||||
FinishRequest(request, clientResult);
|
||||
}
|
||||
|
||||
if (clientHeader.ReceiveListType < 2 &&
|
||||
|
@ -770,6 +770,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
|
||||
PointerBufferDesc descriptor = new PointerBufferDesc(pointerDesc);
|
||||
|
||||
ulong recvListBufferAddress = 0;
|
||||
|
||||
if (descriptor.BufferSize != 0)
|
||||
{
|
||||
clientResult = GetReceiveListAddress(
|
||||
|
@ -778,8 +780,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
clientHeader.ReceiveListType,
|
||||
serverHeader.MessageSizeInWords,
|
||||
receiveList,
|
||||
ref recvListDstOffset,
|
||||
out ulong recvListBufferAddress);
|
||||
ref recvListDstOffset,
|
||||
out recvListBufferAddress);
|
||||
|
||||
if (clientResult != KernelResult.Success)
|
||||
{
|
||||
|
@ -806,6 +808,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
}
|
||||
}
|
||||
|
||||
ulong dstDescAddress = clientMsg.DramAddress + offset * 4;
|
||||
|
||||
ulong clientPointerDesc =
|
||||
(recvListBufferAddress << 32) |
|
||||
((recvListBufferAddress >> 20) & 0xf000) |
|
||||
((recvListBufferAddress >> 30) & 0xffc0);
|
||||
|
||||
clientPointerDesc |= pointerDesc & 0xffff000f;
|
||||
|
||||
KernelContext.Memory.Write(dstDescAddress + 0, clientPointerDesc);
|
||||
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
|
@ -860,16 +873,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
}
|
||||
|
||||
// Unmap buffers from server.
|
||||
clientResult = request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager);
|
||||
|
||||
if (clientResult != KernelResult.Success)
|
||||
{
|
||||
CleanUpForError();
|
||||
|
||||
return serverResult;
|
||||
}
|
||||
|
||||
WakeClientThread(request, clientResult);
|
||||
FinishRequest(request, clientResult);
|
||||
|
||||
return serverResult;
|
||||
}
|
||||
|
@ -1109,7 +1113,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
{
|
||||
foreach (KSessionRequest request in IterateWithRemovalOfAllRequests())
|
||||
{
|
||||
CancelRequest(request, KernelResult.PortRemoteClosed);
|
||||
FinishRequest(request, KernelResult.PortRemoteClosed);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1180,7 +1184,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
return hasRequest;
|
||||
}
|
||||
|
||||
private void CancelRequest(KSessionRequest request, KernelResult result)
|
||||
private void FinishRequest(KSessionRequest request, KernelResult result)
|
||||
{
|
||||
KProcess clientProcess = request.ClientThread.Owner;
|
||||
KProcess serverProcess = request.ServerProcess;
|
||||
|
@ -1221,14 +1225,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
{
|
||||
KProcess clientProcess = request.ClientThread.Owner;
|
||||
|
||||
ulong address = clientProcess.MemoryManager.GetDramAddressFromVa(request.CustomCmdBuffAddr);
|
||||
if (result != KernelResult.Success)
|
||||
{
|
||||
ulong address = clientProcess.MemoryManager.GetDramAddressFromVa(request.CustomCmdBuffAddr);
|
||||
|
||||
KernelContext.Memory.Write<ulong>(address, 0);
|
||||
KernelContext.Memory.Write(address + 8, (int)result);
|
||||
KernelContext.Memory.Write<ulong>(address, 0);
|
||||
KernelContext.Memory.Write(address + 8, (int)result);
|
||||
}
|
||||
|
||||
clientProcess.MemoryManager.UnborrowIpcBuffer(
|
||||
request.CustomCmdBuffAddr,
|
||||
request.CustomCmdBuffSize);
|
||||
clientProcess.MemoryManager.UnborrowIpcBuffer(request.CustomCmdBuffAddr, request.CustomCmdBuffSize);
|
||||
|
||||
request.AsyncEvent.Signal();
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
|
||||
private bool _hasBeenInitialized;
|
||||
|
||||
public KSession(KernelContext context) : base(context)
|
||||
public KSession(KernelContext context, KClientPort parentPort = null) : base(context)
|
||||
{
|
||||
ServerSession = new KServerSession(context, this);
|
||||
ClientSession = new KClientSession(context, this);
|
||||
ClientSession = new KClientSession(context, this, parentPort);
|
||||
|
||||
_hasBeenInitialized = true;
|
||||
}
|
||||
|
@ -54,10 +54,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
{
|
||||
if (_hasBeenInitialized)
|
||||
{
|
||||
ClientSession.DisconnectFromPort();
|
||||
|
||||
KProcess creatorProcess = ClientSession.CreatorProcess;
|
||||
|
||||
creatorProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
|
||||
|
||||
creatorProcess.DecrementReferenceCount();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,13 +17,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||
public ulong CustomCmdBuffSize { get; }
|
||||
|
||||
public KSessionRequest(
|
||||
KThread clientThread,
|
||||
ulong customCmdBuffAddr,
|
||||
ulong customCmdBuffSize)
|
||||
KThread clientThread,
|
||||
ulong customCmdBuffAddr,
|
||||
ulong customCmdBuffSize,
|
||||
KWritableEvent asyncEvent = null)
|
||||
{
|
||||
ClientThread = clientThread;
|
||||
CustomCmdBuffAddr = customCmdBuffAddr;
|
||||
CustomCmdBuffSize = customCmdBuffSize;
|
||||
AsyncEvent = asyncEvent;
|
||||
|
||||
BufferDescriptorTable = new KBufferDescriptorTable();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue