mirror of
https://github.com/ryujinx-mirror/ryujinx.git
synced 2024-11-28 10:00:50 +01:00
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
@ -115,19 +115,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
|
|
||||||
ulong clientEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KMemoryManager.PageSize);
|
ulong clientEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KMemoryManager.PageSize);
|
||||||
ulong clientEndAddrRounded = BitUtils.AlignUp (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,
|
clientEndAddrTruncated,
|
||||||
clientEndAddr - clientEndAddrTruncated,
|
clientEndAddr - clientEndAddrTruncated,
|
||||||
serverEndAddrTruncated,
|
|
||||||
stateMask,
|
stateMask,
|
||||||
stateMask,
|
stateMask,
|
||||||
MemoryPermission.ReadAndWrite,
|
MemoryPermission.ReadAndWrite,
|
||||||
attributeMask,
|
attributeMask,
|
||||||
MemoryAttribute.None);
|
MemoryAttribute.None,
|
||||||
|
serverEndAddrTruncated);
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
|
@ -1,21 +1,19 @@
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Services;
|
using Ryujinx.HLE.HOS.Services;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
{
|
{
|
||||||
class KClientPort : KSynchronizationObject
|
class KClientPort : KSynchronizationObject
|
||||||
{
|
{
|
||||||
private int _sessionsCount;
|
private int _sessionsCount;
|
||||||
private int _currentCapacity;
|
|
||||||
private readonly int _maxSessions;
|
private readonly int _maxSessions;
|
||||||
|
|
||||||
private readonly KPort _parent;
|
private readonly KPort _parent;
|
||||||
|
|
||||||
public bool IsLight => _parent.IsLight;
|
public bool IsLight => _parent.IsLight;
|
||||||
|
|
||||||
private readonly object _countIncLock;
|
|
||||||
|
|
||||||
// TODO: Remove that, we need it for now to allow HLE
|
// TODO: Remove that, we need it for now to allow HLE
|
||||||
// SM implementation to work with the new IPC system.
|
// SM implementation to work with the new IPC system.
|
||||||
public IpcService Service { get; set; }
|
public IpcService Service { get; set; }
|
||||||
@ -24,8 +22,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
{
|
{
|
||||||
_maxSessions = maxSessions;
|
_maxSessions = maxSessions;
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
|
|
||||||
_countIncLock = new object();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult Connect(out KClientSession clientSession)
|
public KernelResult Connect(out KClientSession clientSession)
|
||||||
@ -40,26 +36,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
return KernelResult.ResLimitExceeded;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_currentCapacity < _sessionsCount)
|
KSession session = new KSession(KernelContext, this);
|
||||||
{
|
|
||||||
_currentCapacity = _sessionsCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
KSession session = new KSession(KernelContext);
|
|
||||||
|
|
||||||
if (Service != null)
|
if (Service != null)
|
||||||
{
|
{
|
||||||
@ -93,19 +77,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
return KernelResult.ResLimitExceeded;
|
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);
|
KLightSession session = new KLightSession(KernelContext);
|
||||||
|
|
||||||
@ -124,6 +101,43 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
return result;
|
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)
|
public new static KernelResult RemoveName(KernelContext context, string name)
|
||||||
{
|
{
|
||||||
KAutoObject foundObj = FindNamedObject(context, name);
|
KAutoObject foundObj = FindNamedObject(context, name);
|
||||||
|
@ -13,18 +13,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
|
|
||||||
public ChannelState State { get; set; }
|
public ChannelState State { get; set; }
|
||||||
|
|
||||||
|
public KClientPort ParentPort { get; }
|
||||||
|
|
||||||
// TODO: Remove that, we need it for now to allow HLE
|
// TODO: Remove that, we need it for now to allow HLE
|
||||||
// services implementation to work with the new IPC system.
|
// services implementation to work with the new IPC system.
|
||||||
public IpcService Service { get; set; }
|
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;
|
State = ChannelState.Open;
|
||||||
|
|
||||||
CreatorProcess = context.Scheduler.GetCurrentProcess();
|
CreatorProcess = context.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
CreatorProcess.IncrementReferenceCount();
|
CreatorProcess.IncrementReferenceCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +55,30 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
return result;
|
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()
|
protected override void Destroy()
|
||||||
{
|
{
|
||||||
_parent.DisconnectClient();
|
_parent.DisconnectClient();
|
||||||
|
@ -633,7 +633,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
{
|
{
|
||||||
CloseAllHandles(clientMsg, serverHeader, clientProcess);
|
CloseAllHandles(clientMsg, serverHeader, clientProcess);
|
||||||
|
|
||||||
CancelRequest(request, clientResult);
|
FinishRequest(request, clientResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientHeader.ReceiveListType < 2 &&
|
if (clientHeader.ReceiveListType < 2 &&
|
||||||
@ -770,6 +770,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
|
|
||||||
PointerBufferDesc descriptor = new PointerBufferDesc(pointerDesc);
|
PointerBufferDesc descriptor = new PointerBufferDesc(pointerDesc);
|
||||||
|
|
||||||
|
ulong recvListBufferAddress = 0;
|
||||||
|
|
||||||
if (descriptor.BufferSize != 0)
|
if (descriptor.BufferSize != 0)
|
||||||
{
|
{
|
||||||
clientResult = GetReceiveListAddress(
|
clientResult = GetReceiveListAddress(
|
||||||
@ -779,7 +781,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
serverHeader.MessageSizeInWords,
|
serverHeader.MessageSizeInWords,
|
||||||
receiveList,
|
receiveList,
|
||||||
ref recvListDstOffset,
|
ref recvListDstOffset,
|
||||||
out ulong recvListBufferAddress);
|
out recvListBufferAddress);
|
||||||
|
|
||||||
if (clientResult != KernelResult.Success)
|
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;
|
offset += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -860,16 +873,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unmap buffers from server.
|
// Unmap buffers from server.
|
||||||
clientResult = request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager);
|
FinishRequest(request, clientResult);
|
||||||
|
|
||||||
if (clientResult != KernelResult.Success)
|
|
||||||
{
|
|
||||||
CleanUpForError();
|
|
||||||
|
|
||||||
return serverResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
WakeClientThread(request, clientResult);
|
|
||||||
|
|
||||||
return serverResult;
|
return serverResult;
|
||||||
}
|
}
|
||||||
@ -1109,7 +1113,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
{
|
{
|
||||||
foreach (KSessionRequest request in IterateWithRemovalOfAllRequests())
|
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;
|
return hasRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelRequest(KSessionRequest request, KernelResult result)
|
private void FinishRequest(KSessionRequest request, KernelResult result)
|
||||||
{
|
{
|
||||||
KProcess clientProcess = request.ClientThread.Owner;
|
KProcess clientProcess = request.ClientThread.Owner;
|
||||||
KProcess serverProcess = request.ServerProcess;
|
KProcess serverProcess = request.ServerProcess;
|
||||||
@ -1221,14 +1225,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
{
|
{
|
||||||
KProcess clientProcess = request.ClientThread.Owner;
|
KProcess clientProcess = request.ClientThread.Owner;
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
ulong address = clientProcess.MemoryManager.GetDramAddressFromVa(request.CustomCmdBuffAddr);
|
ulong address = clientProcess.MemoryManager.GetDramAddressFromVa(request.CustomCmdBuffAddr);
|
||||||
|
|
||||||
KernelContext.Memory.Write<ulong>(address, 0);
|
KernelContext.Memory.Write<ulong>(address, 0);
|
||||||
KernelContext.Memory.Write(address + 8, (int)result);
|
KernelContext.Memory.Write(address + 8, (int)result);
|
||||||
|
}
|
||||||
|
|
||||||
clientProcess.MemoryManager.UnborrowIpcBuffer(
|
clientProcess.MemoryManager.UnborrowIpcBuffer(request.CustomCmdBuffAddr, request.CustomCmdBuffSize);
|
||||||
request.CustomCmdBuffAddr,
|
|
||||||
request.CustomCmdBuffSize);
|
|
||||||
|
|
||||||
request.AsyncEvent.Signal();
|
request.AsyncEvent.Signal();
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
|
|
||||||
private bool _hasBeenInitialized;
|
private bool _hasBeenInitialized;
|
||||||
|
|
||||||
public KSession(KernelContext context) : base(context)
|
public KSession(KernelContext context, KClientPort parentPort = null) : base(context)
|
||||||
{
|
{
|
||||||
ServerSession = new KServerSession(context, this);
|
ServerSession = new KServerSession(context, this);
|
||||||
ClientSession = new KClientSession(context, this);
|
ClientSession = new KClientSession(context, this, parentPort);
|
||||||
|
|
||||||
_hasBeenInitialized = true;
|
_hasBeenInitialized = true;
|
||||||
}
|
}
|
||||||
@ -54,10 +54,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
{
|
{
|
||||||
if (_hasBeenInitialized)
|
if (_hasBeenInitialized)
|
||||||
{
|
{
|
||||||
|
ClientSession.DisconnectFromPort();
|
||||||
|
|
||||||
KProcess creatorProcess = ClientSession.CreatorProcess;
|
KProcess creatorProcess = ClientSession.CreatorProcess;
|
||||||
|
|
||||||
creatorProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
|
creatorProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
|
||||||
|
|
||||||
creatorProcess.DecrementReferenceCount();
|
creatorProcess.DecrementReferenceCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
|||||||
public KSessionRequest(
|
public KSessionRequest(
|
||||||
KThread clientThread,
|
KThread clientThread,
|
||||||
ulong customCmdBuffAddr,
|
ulong customCmdBuffAddr,
|
||||||
ulong customCmdBuffSize)
|
ulong customCmdBuffSize,
|
||||||
|
KWritableEvent asyncEvent = null)
|
||||||
{
|
{
|
||||||
ClientThread = clientThread;
|
ClientThread = clientThread;
|
||||||
CustomCmdBuffAddr = customCmdBuffAddr;
|
CustomCmdBuffAddr = customCmdBuffAddr;
|
||||||
CustomCmdBuffSize = customCmdBuffSize;
|
CustomCmdBuffSize = customCmdBuffSize;
|
||||||
|
AsyncEvent = asyncEvent;
|
||||||
|
|
||||||
BufferDescriptorTable = new KBufferDescriptorTable();
|
BufferDescriptorTable = new KBufferDescriptorTable();
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
Attribute |= attribute;
|
Attribute |= attribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetIpcMappingPermission(MemoryPermission permission)
|
public void SetIpcMappingPermission(MemoryPermission newPermission)
|
||||||
{
|
{
|
||||||
int oldIpcRefCount = IpcRefCount++;
|
int oldIpcRefCount = IpcRefCount++;
|
||||||
|
|
||||||
@ -52,10 +52,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
if (oldIpcRefCount == 0)
|
if (oldIpcRefCount == 0)
|
||||||
{
|
{
|
||||||
SourcePermission = permission;
|
SourcePermission = Permission;
|
||||||
|
|
||||||
Permission &= ~MemoryPermission.ReadAndWrite;
|
Permission &= ~MemoryPermission.ReadAndWrite;
|
||||||
Permission |= MemoryPermission.ReadAndWrite & permission;
|
Permission |= MemoryPermission.ReadAndWrite & newPermission;
|
||||||
}
|
}
|
||||||
|
|
||||||
Attribute |= MemoryAttribute.IpcMapped;
|
Attribute |= MemoryAttribute.IpcMapped;
|
||||||
|
@ -1688,6 +1688,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
}
|
}
|
||||||
|
|
||||||
ulong addressRounded = BitUtils.AlignUp (address, PageSize);
|
ulong addressRounded = BitUtils.AlignUp (address, PageSize);
|
||||||
|
ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
|
||||||
ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
|
ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
|
||||||
ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
|
ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
|
||||||
|
|
||||||
@ -1700,9 +1701,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
void CleanUpForError()
|
void CleanUpForError()
|
||||||
{
|
{
|
||||||
|
if (visitedSize == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ulong endAddrVisited = address + visitedSize;
|
ulong endAddrVisited = address + visitedSize;
|
||||||
|
|
||||||
foreach (KMemoryInfo info in IterateOverRange(address, endAddrVisited))
|
foreach (KMemoryInfo info in IterateOverRange(addressRounded, endAddrVisited))
|
||||||
{
|
{
|
||||||
if ((info.Permission & MemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
|
if ((info.Permission & MemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
|
||||||
{
|
{
|
||||||
@ -1729,7 +1735,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
{
|
{
|
||||||
KernelResult result;
|
KernelResult result;
|
||||||
|
|
||||||
foreach (KMemoryInfo info in IterateOverRange(address, endAddrRounded))
|
if (addressRounded < endAddrTruncated)
|
||||||
|
{
|
||||||
|
foreach (KMemoryInfo info in IterateOverRange(addressTruncated, endAddrRounded))
|
||||||
{
|
{
|
||||||
// Check if the block state matches what we expect.
|
// Check if the block state matches what we expect.
|
||||||
if ((info.State & stateMask) != stateMask ||
|
if ((info.State & stateMask) != stateMask ||
|
||||||
@ -1766,6 +1774,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
visitedSize += blockSize;
|
visitedSize += blockSize;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result = GetPagesForIpcTransfer(address, size, copyData, aslrDisabled, region, out pageList);
|
result = GetPagesForIpcTransfer(address, size, copyData, aslrDisabled, region, out pageList);
|
||||||
|
|
||||||
@ -1778,7 +1787,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
if (visitedSize != 0)
|
if (visitedSize != 0)
|
||||||
{
|
{
|
||||||
InsertBlock(address, visitedSize / PageSize, SetIpcMappingPermissions, permissionMask);
|
InsertBlock(addressRounded, visitedSize / PageSize, SetIpcMappingPermissions, permissionMask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1793,25 +1802,31 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
MemoryRegion region,
|
MemoryRegion region,
|
||||||
out KPageList pageList)
|
out KPageList pageList)
|
||||||
{
|
{
|
||||||
|
// When the start address is unaligned, we can't safely map the
|
||||||
|
// first page as it would expose other undesirable information on the
|
||||||
|
// target process. So, instead we allocate new pages, copy the data
|
||||||
|
// inside the range, and then clear the remaining space.
|
||||||
|
// The same also holds for the last page, if the end address
|
||||||
|
// (address + size) is also not aligned.
|
||||||
|
|
||||||
pageList = null;
|
pageList = null;
|
||||||
|
|
||||||
|
KPageList pages = new KPageList();
|
||||||
|
|
||||||
ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
|
ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
|
||||||
ulong addressRounded = BitUtils.AlignUp (address, PageSize);
|
ulong addressRounded = BitUtils.AlignUp (address, PageSize);
|
||||||
|
|
||||||
ulong endAddr = address + size;
|
ulong endAddr = address + size;
|
||||||
|
|
||||||
ulong dstFirstPagePa = AllocateSinglePage(region, aslrDisabled);
|
ulong dstFirstPagePa = 0;
|
||||||
|
|
||||||
if (dstFirstPagePa == 0)
|
|
||||||
{
|
|
||||||
return KernelResult.OutOfMemory;
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong dstLastPagePa = 0;
|
ulong dstLastPagePa = 0;
|
||||||
|
|
||||||
void CleanUpForError()
|
void CleanUpForError()
|
||||||
|
{
|
||||||
|
if (dstFirstPagePa != 0)
|
||||||
{
|
{
|
||||||
FreeSinglePage(region, dstFirstPagePa);
|
FreeSinglePage(region, dstFirstPagePa);
|
||||||
|
}
|
||||||
|
|
||||||
if (dstLastPagePa != 0)
|
if (dstLastPagePa != 0)
|
||||||
{
|
{
|
||||||
@ -1819,9 +1834,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Is the first page address aligned?
|
||||||
|
// If not, allocate a new page and copy the unaligned chunck.
|
||||||
|
if (addressTruncated < addressRounded)
|
||||||
|
{
|
||||||
|
dstFirstPagePa = AllocateSinglePage(region, aslrDisabled);
|
||||||
|
|
||||||
|
if (dstFirstPagePa == 0)
|
||||||
|
{
|
||||||
|
return KernelResult.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
ulong firstPageFillAddress = dstFirstPagePa;
|
ulong firstPageFillAddress = dstFirstPagePa;
|
||||||
|
|
||||||
if (!ConvertVaToPa(addressTruncated, out ulong srcFirstPagePa))
|
if (!TryConvertVaToPa(addressTruncated, out ulong srcFirstPagePa))
|
||||||
{
|
{
|
||||||
CleanUpForError();
|
CleanUpForError();
|
||||||
|
|
||||||
@ -1830,12 +1856,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
ulong unusedSizeAfter;
|
ulong unusedSizeAfter;
|
||||||
|
|
||||||
// When the start address is unaligned, we can't safely map the
|
|
||||||
// first page as it would expose other undesirable information on the
|
|
||||||
// target process. So, instead we allocate new pages, copy the data
|
|
||||||
// inside the range, and then clear the remaining space.
|
|
||||||
// The same also holds for the last page, if the end address
|
|
||||||
// (address + size) is also not aligned.
|
|
||||||
if (copyData)
|
if (copyData)
|
||||||
{
|
{
|
||||||
ulong unusedSizeBefore = address - addressTruncated;
|
ulong unusedSizeBefore = address - addressTruncated;
|
||||||
@ -1862,14 +1882,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
_context.Memory.ZeroFill(firstPageFillAddress, unusedSizeAfter);
|
_context.Memory.ZeroFill(firstPageFillAddress, unusedSizeAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
KPageList pages = new KPageList();
|
|
||||||
|
|
||||||
if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success)
|
if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success)
|
||||||
{
|
{
|
||||||
CleanUpForError();
|
CleanUpForError();
|
||||||
|
|
||||||
return KernelResult.OutOfResource;
|
return KernelResult.OutOfResource;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
|
ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
|
||||||
ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
|
ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
|
||||||
@ -1881,9 +1900,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
AddVaRangeToPageList(pages, addressRounded, alignedPagesCount);
|
AddVaRangeToPageList(pages, addressRounded, alignedPagesCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (endAddrTruncated != endAddrRounded)
|
// Is the last page end address aligned?
|
||||||
|
// If not, allocate a new page and copy the unaligned chunck.
|
||||||
|
if (endAddrTruncated < endAddrRounded && (addressTruncated == addressRounded || addressTruncated < endAddrTruncated))
|
||||||
{
|
{
|
||||||
// End is also not aligned...
|
|
||||||
dstLastPagePa = AllocateSinglePage(region, aslrDisabled);
|
dstLastPagePa = AllocateSinglePage(region, aslrDisabled);
|
||||||
|
|
||||||
if (dstLastPagePa == 0)
|
if (dstLastPagePa == 0)
|
||||||
@ -1895,13 +1915,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
ulong lastPageFillAddr = dstLastPagePa;
|
ulong lastPageFillAddr = dstLastPagePa;
|
||||||
|
|
||||||
if (!ConvertVaToPa(endAddrTruncated, out ulong srcLastPagePa))
|
if (!TryConvertVaToPa(endAddrTruncated, out ulong srcLastPagePa))
|
||||||
{
|
{
|
||||||
CleanUpForError();
|
CleanUpForError();
|
||||||
|
|
||||||
return KernelResult.InvalidMemState;
|
return KernelResult.InvalidMemState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ulong unusedSizeAfter;
|
||||||
|
|
||||||
if (copyData)
|
if (copyData)
|
||||||
{
|
{
|
||||||
ulong copySize = endAddr - endAddrTruncated;
|
ulong copySize = endAddr - endAddrTruncated;
|
||||||
@ -1921,7 +1943,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
_context.Memory.ZeroFill(lastPageFillAddr, unusedSizeAfter);
|
_context.Memory.ZeroFill(lastPageFillAddr, unusedSizeAfter);
|
||||||
|
|
||||||
if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success)
|
if (pages.AddRange(dstLastPagePa, 1) != KernelResult.Success)
|
||||||
{
|
{
|
||||||
CleanUpForError();
|
CleanUpForError();
|
||||||
|
|
||||||
@ -1954,9 +1976,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
MemoryPermission permission,
|
MemoryPermission permission,
|
||||||
MemoryState state,
|
MemoryState state,
|
||||||
KPageList pageList,
|
KPageList pageList,
|
||||||
out ulong mappedVa)
|
out ulong dst)
|
||||||
{
|
{
|
||||||
mappedVa = 0;
|
dst = 0;
|
||||||
|
|
||||||
lock (_blocks)
|
lock (_blocks)
|
||||||
{
|
{
|
||||||
@ -2002,7 +2024,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
InsertBlock(va, neededPagesCount, state, permission);
|
InsertBlock(va, neededPagesCount, state, permission);
|
||||||
|
|
||||||
mappedVa = va;
|
dst = va + (address - addressTruncated);
|
||||||
}
|
}
|
||||||
|
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
@ -2044,6 +2066,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
}
|
}
|
||||||
|
|
||||||
ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
|
ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
|
||||||
|
ulong addressRounded = BitUtils.AlignUp (address, PageSize);
|
||||||
|
ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
|
||||||
ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
|
ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
|
||||||
|
|
||||||
ulong pagesCount = (endAddrRounded - addressTruncated) / PageSize;
|
ulong pagesCount = (endAddrRounded - addressTruncated) / PageSize;
|
||||||
@ -2056,6 +2080,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
MemoryPermission.None,
|
MemoryPermission.None,
|
||||||
MemoryOperation.Unmap);
|
MemoryOperation.Unmap);
|
||||||
|
|
||||||
|
// Free pages we had to create on-demand, if any of the buffer was not page aligned.
|
||||||
|
// Real kernel has page ref counting, so this is done as part of the unmap operation.
|
||||||
|
if (addressTruncated != addressRounded)
|
||||||
|
{
|
||||||
|
FreeSinglePage(_memRegion, ConvertVaToPa(addressTruncated));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endAddrTruncated < endAddrRounded && (addressTruncated == addressRounded || addressTruncated < endAddrTruncated))
|
||||||
|
{
|
||||||
|
FreeSinglePage(_memRegion, ConvertVaToPa(endAddrTruncated));
|
||||||
|
}
|
||||||
|
|
||||||
if (result == KernelResult.Success)
|
if (result == KernelResult.Success)
|
||||||
{
|
{
|
||||||
InsertBlock(addressTruncated, pagesCount, MemoryState.Unmapped);
|
InsertBlock(addressTruncated, pagesCount, MemoryState.Unmapped);
|
||||||
@ -2107,7 +2143,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
lock (_blocks)
|
lock (_blocks)
|
||||||
{
|
{
|
||||||
foreach (KMemoryInfo info in IterateOverRange(address, endAddrTruncated))
|
foreach (KMemoryInfo info in IterateOverRange(addressRounded, endAddrTruncated))
|
||||||
{
|
{
|
||||||
// Check if the block state matches what we expect.
|
// Check if the block state matches what we expect.
|
||||||
if ((info.State & stateMask) != stateMask ||
|
if ((info.State & stateMask) != stateMask ||
|
||||||
@ -2139,11 +2175,113 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InsertBlock(address, pagesCount, RestoreIpcMappingPermissions);
|
InsertBlock(addressRounded, pagesCount, RestoreIpcMappingPermissions);
|
||||||
|
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KernelResult BorrowIpcBuffer(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
return SetAttributesAndChangePermission(
|
||||||
|
address,
|
||||||
|
size,
|
||||||
|
MemoryState.IpcBufferAllowed,
|
||||||
|
MemoryState.IpcBufferAllowed,
|
||||||
|
MemoryPermission.Mask,
|
||||||
|
MemoryPermission.ReadAndWrite,
|
||||||
|
MemoryAttribute.Mask,
|
||||||
|
MemoryAttribute.None,
|
||||||
|
MemoryPermission.None,
|
||||||
|
MemoryAttribute.Borrowed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KernelResult SetAttributesAndChangePermission(
|
||||||
|
ulong address,
|
||||||
|
ulong size,
|
||||||
|
MemoryState stateMask,
|
||||||
|
MemoryState stateExpected,
|
||||||
|
MemoryPermission permissionMask,
|
||||||
|
MemoryPermission permissionExpected,
|
||||||
|
MemoryAttribute attributeMask,
|
||||||
|
MemoryAttribute attributeExpected,
|
||||||
|
MemoryPermission newPermission,
|
||||||
|
MemoryAttribute attributeSetMask,
|
||||||
|
KPageList pageList = null)
|
||||||
|
{
|
||||||
|
if (address + size <= address || !InsideAddrSpace(address, size))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_blocks)
|
||||||
|
{
|
||||||
|
if (CheckRange(
|
||||||
|
address,
|
||||||
|
size,
|
||||||
|
stateMask | MemoryState.IsPoolAllocated,
|
||||||
|
stateExpected | MemoryState.IsPoolAllocated,
|
||||||
|
permissionMask,
|
||||||
|
permissionExpected,
|
||||||
|
attributeMask,
|
||||||
|
attributeExpected,
|
||||||
|
MemoryAttribute.IpcAndDeviceMapped,
|
||||||
|
out MemoryState oldState,
|
||||||
|
out MemoryPermission oldPermission,
|
||||||
|
out MemoryAttribute oldAttribute))
|
||||||
|
{
|
||||||
|
ulong pagesCount = size / PageSize;
|
||||||
|
|
||||||
|
if (pageList != null)
|
||||||
|
{
|
||||||
|
KPageList currPageList = new KPageList();
|
||||||
|
|
||||||
|
AddVaRangeToPageList(currPageList, address, pagesCount);
|
||||||
|
|
||||||
|
if (!currPageList.IsEqual(pageList))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
|
||||||
|
{
|
||||||
|
return KernelResult.OutOfResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPermission == MemoryPermission.None)
|
||||||
|
{
|
||||||
|
newPermission = oldPermission;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPermission != oldPermission)
|
||||||
|
{
|
||||||
|
KernelResult result = DoMmuOperation(
|
||||||
|
address,
|
||||||
|
pagesCount,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
newPermission,
|
||||||
|
MemoryOperation.ChangePermRw);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryAttribute newAttribute = oldAttribute | attributeSetMask;
|
||||||
|
|
||||||
|
InsertBlock(address, pagesCount, oldState, newPermission, newAttribute);
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public KernelResult UnborrowIpcBuffer(ulong address, ulong size)
|
public KernelResult UnborrowIpcBuffer(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
return ClearAttributesAndChangePermission(
|
return ClearAttributesAndChangePermission(
|
||||||
@ -2172,6 +2310,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
MemoryAttribute attributeClearMask,
|
MemoryAttribute attributeClearMask,
|
||||||
KPageList pageList = null)
|
KPageList pageList = null)
|
||||||
{
|
{
|
||||||
|
if (address + size <= address || !InsideAddrSpace(address, size))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
|
||||||
lock (_blocks)
|
lock (_blocks)
|
||||||
{
|
{
|
||||||
if (CheckRange(
|
if (CheckRange(
|
||||||
@ -2247,7 +2390,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
|
|
||||||
while (address < start + pagesCount * PageSize)
|
while (address < start + pagesCount * PageSize)
|
||||||
{
|
{
|
||||||
if (!ConvertVaToPa(address, out ulong pa))
|
if (!TryConvertVaToPa(address, out ulong pa))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Unexpected failure translating virtual address.");
|
throw new InvalidOperationException("Unexpected failure translating virtual address.");
|
||||||
}
|
}
|
||||||
@ -3114,7 +3257,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||||||
return _cpuMemory.GetPhysicalAddress(va);
|
return _cpuMemory.GetPhysicalAddress(va);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ConvertVaToPa(ulong va, out ulong pa)
|
public ulong ConvertVaToPa(ulong va)
|
||||||
|
{
|
||||||
|
if (!TryConvertVaToPa(va, out ulong pa))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Invalid virtual address 0x{va:X} specified.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pa;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryConvertVaToPa(ulong va, out ulong pa)
|
||||||
{
|
{
|
||||||
pa = DramMemoryMap.DramBase + _cpuMemory.GetPhysicalAddress(va);
|
pa = DramMemoryMap.DramBase + _cpuMemory.GetPhysicalAddress(va);
|
||||||
|
|
||||||
|
@ -544,7 +544,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
private KernelResult FreeTlsPage(KTlsPageInfo pageInfo)
|
private KernelResult FreeTlsPage(KTlsPageInfo pageInfo)
|
||||||
{
|
{
|
||||||
if (!MemoryManager.ConvertVaToPa(pageInfo.PageAddr, out ulong tlsPagePa))
|
if (!MemoryManager.TryConvertVaToPa(pageInfo.PageAddr, out ulong tlsPagePa))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Unexpected failure translating virtual address to physical.");
|
throw new InvalidOperationException("Unexpected failure translating virtual address to physical.");
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using ARMeilleure.Memory;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
@ -96,16 +95,25 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult SendSyncRequest(int handle)
|
public KernelResult SendSyncRequestHLE(int handle)
|
||||||
{
|
|
||||||
return SendSyncRequestWithUserBuffer((ulong)_context.Scheduler.GetCurrentThread().Context.Tpidr, 0x100, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelResult SendSyncRequestWithUserBuffer(ulong messagePtr, ulong size, int handle)
|
|
||||||
{
|
{
|
||||||
KProcess process = _context.Scheduler.GetCurrentProcess();
|
KProcess process = _context.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
byte[] messageData = new byte[size];
|
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);
|
process.CpuMemory.Read(messagePtr, messageData);
|
||||||
|
|
||||||
@ -113,7 +121,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
|
|
||||||
if (clientSession == null || clientSession.Service == null)
|
if (clientSession == null || clientSession.Service == null)
|
||||||
{
|
{
|
||||||
return SendSyncRequest_(handle);
|
return SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientSession != null)
|
if (clientSession != null)
|
||||||
@ -168,7 +176,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
ipcMessage.Thread.Reschedule(ThreadSchedState.Running);
|
ipcMessage.Thread.Reschedule(ThreadSchedState.Running);
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult SendSyncRequest_(int handle)
|
private KernelResult SendSyncRequest(int handle)
|
||||||
{
|
{
|
||||||
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
|
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
@ -182,6 +190,123 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return session.SendSyncRequest();
|
return session.SendSyncRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KernelResult SendSyncRequestWithUserBuffer(ulong messagePtr, ulong messageSize, int handle)
|
||||||
|
{
|
||||||
|
if (!PageAligned(messagePtr))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PageAligned(messageSize) || messageSize == 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messagePtr + messageSize <= messagePtr)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle);
|
||||||
|
|
||||||
|
if (session == null)
|
||||||
|
{
|
||||||
|
result = KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = session.SendSyncRequest(messagePtr, messageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelResult result2 = currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize);
|
||||||
|
|
||||||
|
if (result == KernelResult.Success)
|
||||||
|
{
|
||||||
|
result = result2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult SendAsyncRequestWithUserBuffer(ulong messagePtr, ulong messageSize, int handle, out int doneEventHandle)
|
||||||
|
{
|
||||||
|
doneEventHandle = 0;
|
||||||
|
|
||||||
|
if (!PageAligned(messagePtr))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PageAligned(messageSize) || messageSize == 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messagePtr + messageSize <= messagePtr)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidMemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
KResourceLimit resourceLimit = currentProcess.ResourceLimit;
|
||||||
|
|
||||||
|
if (resourceLimit != null && !resourceLimit.Reserve(LimitableResource.Event, 1))
|
||||||
|
{
|
||||||
|
currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize);
|
||||||
|
|
||||||
|
return KernelResult.ResLimitExceeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle);
|
||||||
|
|
||||||
|
if (session == null)
|
||||||
|
{
|
||||||
|
result = KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
KEvent doneEvent = new KEvent(_context);
|
||||||
|
|
||||||
|
result = currentProcess.HandleTable.GenerateHandle(doneEvent.ReadableEvent, out doneEventHandle);
|
||||||
|
|
||||||
|
if (result == KernelResult.Success)
|
||||||
|
{
|
||||||
|
result = session.SendAsyncRequest(doneEvent.WritableEvent, messagePtr, messageSize);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
currentProcess.HandleTable.CloseHandle(doneEventHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
resourceLimit?.Release(LimitableResource.Event, 1);
|
||||||
|
|
||||||
|
currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public KernelResult CreateSession(
|
public KernelResult CreateSession(
|
||||||
bool isLight,
|
bool isLight,
|
||||||
ulong namePtr,
|
ulong namePtr,
|
||||||
@ -348,7 +473,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
syncObjs[index] = obj;
|
syncObjs[index] = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
KernelResult result;
|
KernelResult result = KernelResult.Success;
|
||||||
|
|
||||||
if (replyTargetHandle != 0)
|
if (replyTargetHandle != 0)
|
||||||
{
|
{
|
||||||
@ -356,17 +481,16 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
|
|
||||||
if (replyTarget == null)
|
if (replyTarget == null)
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidHandle;
|
result = KernelResult.InvalidHandle;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
result = replyTarget.Reply();
|
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
|
||||||
{
|
{
|
||||||
return result;
|
result = replyTarget.Reply();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result == KernelResult.Success)
|
||||||
|
{
|
||||||
while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == KernelResult.Success)
|
while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == KernelResult.Success)
|
||||||
{
|
{
|
||||||
KServerSession session = currentProcess.HandleTable.GetObject<KServerSession>(handles[handleIndex]);
|
KServerSession session = currentProcess.HandleTable.GetObject<KServerSession>(handles[handleIndex]);
|
||||||
@ -381,6 +505,106 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult ReplyAndReceiveWithUserBuffer(
|
||||||
|
ulong handlesPtr,
|
||||||
|
ulong messagePtr,
|
||||||
|
ulong messageSize,
|
||||||
|
int handlesCount,
|
||||||
|
int replyTargetHandle,
|
||||||
|
long timeout,
|
||||||
|
out int handleIndex)
|
||||||
|
{
|
||||||
|
handleIndex = 0;
|
||||||
|
|
||||||
|
if ((uint)handlesCount > 0x40)
|
||||||
|
{
|
||||||
|
return KernelResult.MaximumExceeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
ulong copySize = (ulong)((long)handlesCount * 4);
|
||||||
|
|
||||||
|
if (!currentProcess.MemoryManager.InsideAddrSpace(handlesPtr, copySize))
|
||||||
|
{
|
||||||
|
return KernelResult.UserCopyFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handlesPtr + copySize < handlesPtr)
|
||||||
|
{
|
||||||
|
return KernelResult.UserCopyFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize);
|
||||||
|
|
||||||
|
if (result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] handles = new int[handlesCount];
|
||||||
|
|
||||||
|
if (!KernelTransfer.UserToKernelInt32Array(_context, handlesPtr, handles))
|
||||||
|
{
|
||||||
|
currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize);
|
||||||
|
|
||||||
|
return KernelResult.UserCopyFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
KSynchronizationObject[] syncObjs = new KSynchronizationObject[handlesCount];
|
||||||
|
|
||||||
|
for (int index = 0; index < handlesCount; index++)
|
||||||
|
{
|
||||||
|
KSynchronizationObject obj = currentProcess.HandleTable.GetObject<KSynchronizationObject>(handles[index]);
|
||||||
|
|
||||||
|
if (obj == null)
|
||||||
|
{
|
||||||
|
currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize);
|
||||||
|
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
syncObjs[index] = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (replyTargetHandle != 0)
|
||||||
|
{
|
||||||
|
KServerSession replyTarget = currentProcess.HandleTable.GetObject<KServerSession>(replyTargetHandle);
|
||||||
|
|
||||||
|
if (replyTarget == null)
|
||||||
|
{
|
||||||
|
result = KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = replyTarget.Reply(messagePtr, messageSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == KernelResult.Success)
|
||||||
|
{
|
||||||
|
while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == KernelResult.Success)
|
||||||
|
{
|
||||||
|
KServerSession session = currentProcess.HandleTable.GetObject<KServerSession>(handles[handleIndex]);
|
||||||
|
|
||||||
|
if (session == null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result = session.Receive(messagePtr, messageSize)) != KernelResult.NotFound)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
|
|
||||||
public KernelResult SendSyncRequest32([R(0)] int handle)
|
public KernelResult SendSyncRequest32([R(0)] int handle)
|
||||||
{
|
{
|
||||||
return _syscall.SendSyncRequest(handle);
|
return _syscall.SendSyncRequestHLE(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult SendSyncRequestWithUserBuffer32([R(0)] uint messagePtr, [R(1)] uint size, [R(2)] int handle)
|
public KernelResult SendSyncRequestWithUserBuffer32([R(0)] uint messagePtr, [R(1)] uint messageSize, [R(2)] int handle)
|
||||||
{
|
{
|
||||||
return _syscall.SendSyncRequestWithUserBuffer(messagePtr, size, handle);
|
return _syscall.SendSyncRequestWithUserBufferHLE(messagePtr, messageSize, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult CreateSession32(
|
public KernelResult CreateSession32(
|
||||||
|
@ -22,12 +22,21 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
|
|
||||||
public KernelResult SendSyncRequest64([R(0)] int handle)
|
public KernelResult SendSyncRequest64([R(0)] int handle)
|
||||||
{
|
{
|
||||||
return _syscall.SendSyncRequest(handle);
|
return _syscall.SendSyncRequestHLE(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult SendSyncRequestWithUserBuffer64([R(0)] ulong messagePtr, [R(1)] ulong size, [R(2)] int handle)
|
public KernelResult SendSyncRequestWithUserBuffer64([R(0)] ulong messagePtr, [R(1)] ulong messageSize, [R(2)] int handle)
|
||||||
{
|
{
|
||||||
return _syscall.SendSyncRequestWithUserBuffer(messagePtr, size, handle);
|
return _syscall.SendSyncRequestWithUserBufferHLE(messagePtr, messageSize, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult SendAsyncRequestWithUserBuffer64(
|
||||||
|
[R(1)] ulong messagePtr,
|
||||||
|
[R(2)] ulong messageSize,
|
||||||
|
[R(3)] int handle,
|
||||||
|
[R(1)] out int doneEventHandle)
|
||||||
|
{
|
||||||
|
return _syscall.SendAsyncRequestWithUserBuffer(messagePtr, messageSize, handle, out doneEventHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult CreateSession64(
|
public KernelResult CreateSession64(
|
||||||
@ -54,6 +63,25 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return _syscall.ReplyAndReceive(handlesPtr, handlesCount, replyTargetHandle, timeout, out handleIndex);
|
return _syscall.ReplyAndReceive(handlesPtr, handlesCount, replyTargetHandle, timeout, out handleIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KernelResult ReplyAndReceiveWithUserBuffer64(
|
||||||
|
[R(1)] ulong messagePtr,
|
||||||
|
[R(2)] ulong messageSize,
|
||||||
|
[R(3)] ulong handlesPtr,
|
||||||
|
[R(4)] int handlesCount,
|
||||||
|
[R(5)] int replyTargetHandle,
|
||||||
|
[R(6)] long timeout,
|
||||||
|
[R(1)] out int handleIndex)
|
||||||
|
{
|
||||||
|
return _syscall.ReplyAndReceiveWithUserBuffer(
|
||||||
|
handlesPtr,
|
||||||
|
messagePtr,
|
||||||
|
messageSize,
|
||||||
|
handlesCount,
|
||||||
|
replyTargetHandle,
|
||||||
|
timeout,
|
||||||
|
out handleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
public KernelResult CreatePort64(
|
public KernelResult CreatePort64(
|
||||||
[R(2)] int maxSessions,
|
[R(2)] int maxSessions,
|
||||||
[R(3)] bool isLight,
|
[R(3)] bool isLight,
|
||||||
|
@ -56,6 +56,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
{ 0x1f, nameof(Syscall64.ConnectToNamedPort64) },
|
{ 0x1f, nameof(Syscall64.ConnectToNamedPort64) },
|
||||||
{ 0x21, nameof(Syscall64.SendSyncRequest64) },
|
{ 0x21, nameof(Syscall64.SendSyncRequest64) },
|
||||||
{ 0x22, nameof(Syscall64.SendSyncRequestWithUserBuffer64) },
|
{ 0x22, nameof(Syscall64.SendSyncRequestWithUserBuffer64) },
|
||||||
|
{ 0x23, nameof(Syscall64.SendAsyncRequestWithUserBuffer64) },
|
||||||
{ 0x24, nameof(Syscall64.GetProcessId64) },
|
{ 0x24, nameof(Syscall64.GetProcessId64) },
|
||||||
{ 0x25, nameof(Syscall64.GetThreadId64) },
|
{ 0x25, nameof(Syscall64.GetThreadId64) },
|
||||||
{ 0x26, nameof(Syscall64.Break64) },
|
{ 0x26, nameof(Syscall64.Break64) },
|
||||||
@ -70,6 +71,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
{ 0x40, nameof(Syscall64.CreateSession64) },
|
{ 0x40, nameof(Syscall64.CreateSession64) },
|
||||||
{ 0x41, nameof(Syscall64.AcceptSession64) },
|
{ 0x41, nameof(Syscall64.AcceptSession64) },
|
||||||
{ 0x43, nameof(Syscall64.ReplyAndReceive64) },
|
{ 0x43, nameof(Syscall64.ReplyAndReceive64) },
|
||||||
|
{ 0x44, nameof(Syscall64.ReplyAndReceiveWithUserBuffer64) },
|
||||||
{ 0x45, nameof(Syscall64.CreateEvent64) },
|
{ 0x45, nameof(Syscall64.CreateEvent64) },
|
||||||
{ 0x65, nameof(Syscall64.GetProcessList64) },
|
{ 0x65, nameof(Syscall64.GetProcessList64) },
|
||||||
{ 0x6f, nameof(Syscall64.GetSystemInfo64) },
|
{ 0x6f, nameof(Syscall64.GetSystemInfo64) },
|
||||||
|
@ -127,12 +127,12 @@ namespace Ryujinx.Memory
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copies data from one memory location to another.
|
/// Copies data from one memory location to another.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="srcOffset">Source offset to read the data from</param>
|
|
||||||
/// <param name="dstOffset">Destination offset to write the data into</param>
|
/// <param name="dstOffset">Destination offset to write the data into</param>
|
||||||
|
/// <param name="srcOffset">Source offset to read the data from</param>
|
||||||
/// <param name="size">Size of the copy in bytes</param>
|
/// <param name="size">Size of the copy in bytes</param>
|
||||||
/// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception>
|
/// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception>
|
||||||
/// <exception cref="ArgumentOutOfRangeException">Throw when <paramref name="srcOffset"/>, <paramref name="dstOffset"/> or <paramref name="size"/> is out of range</exception>
|
/// <exception cref="ArgumentOutOfRangeException">Throw when <paramref name="srcOffset"/>, <paramref name="dstOffset"/> or <paramref name="size"/> is out of range</exception>
|
||||||
public void Copy(ulong srcOffset, ulong dstOffset, ulong size)
|
public void Copy(ulong dstOffset, ulong srcOffset, ulong size)
|
||||||
{
|
{
|
||||||
const int MaxChunkSize = 1 << 30;
|
const int MaxChunkSize = 1 << 30;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user