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:
gdkchan 2020-07-17 01:19:07 -03:00 committed by GitHub
parent 46f8cef6a9
commit 9f6b24edfd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 705 additions and 247 deletions

View file

@ -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)
{ {

View file

@ -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) currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
{
_sessionsCount++;
}
else
{
currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
return KernelResult.SessionCountExceeded; return KernelResult.SessionCountExceeded;
}
if (_currentCapacity < _sessionsCount)
{
_currentCapacity = _sessionsCount;
}
} }
KSession session = new KSession(KernelContext); KSession session = new KSession(KernelContext, this);
if (Service != null) if (Service != null)
{ {
@ -93,18 +77,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return KernelResult.ResLimitExceeded; return KernelResult.ResLimitExceeded;
} }
lock (_countIncLock) if (!IncrementSessionsCount())
{ {
if (_sessionsCount < _maxSessions) currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
{
_sessionsCount++;
}
else
{
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);

View file

@ -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();

View file

@ -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(
@ -778,8 +780,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
clientHeader.ReceiveListType, clientHeader.ReceiveListType,
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;
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<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();
} }

View file

@ -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();
} }
} }

View file

@ -17,13 +17,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public ulong CustomCmdBuffSize { get; } public ulong CustomCmdBuffSize { get; }
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();
} }

View file

@ -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;

View file

@ -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,42 +1735,45 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{ {
KernelResult result; KernelResult result;
foreach (KMemoryInfo info in IterateOverRange(address, endAddrRounded)) if (addressRounded < endAddrTruncated)
{ {
// Check if the block state matches what we expect. foreach (KMemoryInfo info in IterateOverRange(addressTruncated, endAddrRounded))
if ((info.State & stateMask) != stateMask ||
(info.Permission & permission) != permission ||
(info.Attribute & attributeMask) != MemoryAttribute.None)
{ {
CleanUpForError(); // Check if the block state matches what we expect.
if ((info.State & stateMask) != stateMask ||
return KernelResult.InvalidMemState; (info.Permission & permission) != permission ||
} (info.Attribute & attributeMask) != MemoryAttribute.None)
ulong blockAddress = GetAddrInRange(info, addressRounded);
ulong blockSize = GetSizeInRange(info, addressRounded, endAddrTruncated);
ulong blockPagesCount = blockSize / PageSize;
if ((info.Permission & MemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
{
result = DoMmuOperation(
blockAddress,
blockPagesCount,
0,
false,
permissionMask,
MemoryOperation.ChangePermRw);
if (result != KernelResult.Success)
{ {
CleanUpForError(); CleanUpForError();
return result; return KernelResult.InvalidMemState;
} }
}
visitedSize += blockSize; ulong blockAddress = GetAddrInRange(info, addressRounded);
ulong blockSize = GetSizeInRange(info, addressRounded, endAddrTruncated);
ulong blockPagesCount = blockSize / PageSize;
if ((info.Permission & MemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
{
result = DoMmuOperation(
blockAddress,
blockPagesCount,
0,
false,
permissionMask,
MemoryOperation.ChangePermRw);
if (result != KernelResult.Success)
{
CleanUpForError();
return result;
}
}
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;
ulong dstLastPagePa = 0;
if (dstFirstPagePa == 0)
{
return KernelResult.OutOfMemory;
}
ulong dstLastPagePa = 0;
void CleanUpForError() void CleanUpForError()
{ {
FreeSinglePage(region, dstFirstPagePa); if (dstFirstPagePa != 0)
{
FreeSinglePage(region, dstFirstPagePa);
}
if (dstLastPagePa != 0) if (dstLastPagePa != 0)
{ {
@ -1819,56 +1834,60 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
} }
} }
ulong firstPageFillAddress = dstFirstPagePa; // Is the first page address aligned?
// If not, allocate a new page and copy the unaligned chunck.
if (!ConvertVaToPa(addressTruncated, out ulong srcFirstPagePa)) if (addressTruncated < addressRounded)
{ {
CleanUpForError(); dstFirstPagePa = AllocateSinglePage(region, aslrDisabled);
return KernelResult.InvalidMemState; if (dstFirstPagePa == 0)
} {
return KernelResult.OutOfMemory;
}
ulong unusedSizeAfter; ulong firstPageFillAddress = dstFirstPagePa;
// When the start address is unaligned, we can't safely map the if (!TryConvertVaToPa(addressTruncated, out ulong srcFirstPagePa))
// first page as it would expose other undesirable information on the {
// target process. So, instead we allocate new pages, copy the data CleanUpForError();
// 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)
{
ulong unusedSizeBefore = address - addressTruncated;
_context.Memory.ZeroFill(dstFirstPagePa, unusedSizeBefore); return KernelResult.InvalidMemState;
}
ulong copySize = addressRounded <= endAddr ? addressRounded - address : size; ulong unusedSizeAfter;
_context.Memory.Copy( if (copyData)
GetDramAddressFromPa(dstFirstPagePa + unusedSizeBefore), {
GetDramAddressFromPa(srcFirstPagePa + unusedSizeBefore), copySize); ulong unusedSizeBefore = address - addressTruncated;
firstPageFillAddress += unusedSizeBefore + copySize; _context.Memory.ZeroFill(dstFirstPagePa, unusedSizeBefore);
unusedSizeAfter = addressRounded > endAddr ? addressRounded - endAddr : 0; ulong copySize = addressRounded <= endAddr ? addressRounded - address : size;
}
else
{
unusedSizeAfter = PageSize;
}
if (unusedSizeAfter != 0) _context.Memory.Copy(
{ GetDramAddressFromPa(dstFirstPagePa + unusedSizeBefore),
_context.Memory.ZeroFill(firstPageFillAddress, unusedSizeAfter); GetDramAddressFromPa(srcFirstPagePa + unusedSizeBefore), copySize);
}
KPageList pages = new KPageList(); firstPageFillAddress += unusedSizeBefore + copySize;
if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success) unusedSizeAfter = addressRounded > endAddr ? addressRounded - endAddr : 0;
{ }
CleanUpForError(); else
{
unusedSizeAfter = PageSize;
}
return KernelResult.OutOfResource; if (unusedSizeAfter != 0)
{
_context.Memory.ZeroFill(firstPageFillAddress, unusedSizeAfter);
}
if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success)
{
CleanUpForError();
return KernelResult.OutOfResource;
}
} }
ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize); ulong endAddrTruncated = BitUtils.AlignDown(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);

View file

@ -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.");
} }

View file

@ -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,32 +481,131 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
if (replyTarget == null) if (replyTarget == null)
{ {
result = KernelResult.InvalidHandle;
}
else
{
result = replyTarget.Reply();
}
}
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()) != KernelResult.NotFound)
{
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; return KernelResult.InvalidHandle;
} }
result = replyTarget.Reply(); syncObjs[index] = obj;
if (result != KernelResult.Success)
{
return result;
}
} }
while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == KernelResult.Success) if (replyTargetHandle != 0)
{ {
KServerSession session = currentProcess.HandleTable.GetObject<KServerSession>(handles[handleIndex]); KServerSession replyTarget = currentProcess.HandleTable.GetObject<KServerSession>(replyTargetHandle);
if (session == null) if (replyTarget == null)
{ {
break; result = KernelResult.InvalidHandle;
} }
else
if ((result = session.Receive()) != KernelResult.NotFound)
{ {
break; 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;
} }

View file

@ -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(

View file

@ -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,

View file

@ -24,62 +24,64 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
Dictionary<int, string> svcFuncs64 = new Dictionary<int, string> Dictionary<int, string> svcFuncs64 = new Dictionary<int, string>
{ {
{ 0x01, nameof(Syscall64.SetHeapSize64) }, { 0x01, nameof(Syscall64.SetHeapSize64) },
{ 0x03, nameof(Syscall64.SetMemoryAttribute64) }, { 0x03, nameof(Syscall64.SetMemoryAttribute64) },
{ 0x04, nameof(Syscall64.MapMemory64) }, { 0x04, nameof(Syscall64.MapMemory64) },
{ 0x05, nameof(Syscall64.UnmapMemory64) }, { 0x05, nameof(Syscall64.UnmapMemory64) },
{ 0x06, nameof(Syscall64.QueryMemory64) }, { 0x06, nameof(Syscall64.QueryMemory64) },
{ 0x07, nameof(Syscall64.ExitProcess64) }, { 0x07, nameof(Syscall64.ExitProcess64) },
{ 0x08, nameof(Syscall64.CreateThread64) }, { 0x08, nameof(Syscall64.CreateThread64) },
{ 0x09, nameof(Syscall64.StartThread64) }, { 0x09, nameof(Syscall64.StartThread64) },
{ 0x0a, nameof(Syscall64.ExitThread64) }, { 0x0a, nameof(Syscall64.ExitThread64) },
{ 0x0b, nameof(Syscall64.SleepThread64) }, { 0x0b, nameof(Syscall64.SleepThread64) },
{ 0x0c, nameof(Syscall64.GetThreadPriority64) }, { 0x0c, nameof(Syscall64.GetThreadPriority64) },
{ 0x0d, nameof(Syscall64.SetThreadPriority64) }, { 0x0d, nameof(Syscall64.SetThreadPriority64) },
{ 0x0e, nameof(Syscall64.GetThreadCoreMask64) }, { 0x0e, nameof(Syscall64.GetThreadCoreMask64) },
{ 0x0f, nameof(Syscall64.SetThreadCoreMask64) }, { 0x0f, nameof(Syscall64.SetThreadCoreMask64) },
{ 0x10, nameof(Syscall64.GetCurrentProcessorNumber64) }, { 0x10, nameof(Syscall64.GetCurrentProcessorNumber64) },
{ 0x11, nameof(Syscall64.SignalEvent64) }, { 0x11, nameof(Syscall64.SignalEvent64) },
{ 0x12, nameof(Syscall64.ClearEvent64) }, { 0x12, nameof(Syscall64.ClearEvent64) },
{ 0x13, nameof(Syscall64.MapSharedMemory64) }, { 0x13, nameof(Syscall64.MapSharedMemory64) },
{ 0x14, nameof(Syscall64.UnmapSharedMemory64) }, { 0x14, nameof(Syscall64.UnmapSharedMemory64) },
{ 0x15, nameof(Syscall64.CreateTransferMemory64) }, { 0x15, nameof(Syscall64.CreateTransferMemory64) },
{ 0x16, nameof(Syscall64.CloseHandle64) }, { 0x16, nameof(Syscall64.CloseHandle64) },
{ 0x17, nameof(Syscall64.ResetSignal64) }, { 0x17, nameof(Syscall64.ResetSignal64) },
{ 0x18, nameof(Syscall64.WaitSynchronization64) }, { 0x18, nameof(Syscall64.WaitSynchronization64) },
{ 0x19, nameof(Syscall64.CancelSynchronization64) }, { 0x19, nameof(Syscall64.CancelSynchronization64) },
{ 0x1a, nameof(Syscall64.ArbitrateLock64) }, { 0x1a, nameof(Syscall64.ArbitrateLock64) },
{ 0x1b, nameof(Syscall64.ArbitrateUnlock64) }, { 0x1b, nameof(Syscall64.ArbitrateUnlock64) },
{ 0x1c, nameof(Syscall64.WaitProcessWideKeyAtomic64) }, { 0x1c, nameof(Syscall64.WaitProcessWideKeyAtomic64) },
{ 0x1d, nameof(Syscall64.SignalProcessWideKey64) }, { 0x1d, nameof(Syscall64.SignalProcessWideKey64) },
{ 0x1e, nameof(Syscall64.GetSystemTick64) }, { 0x1e, nameof(Syscall64.GetSystemTick64) },
{ 0x1f, nameof(Syscall64.ConnectToNamedPort64) }, { 0x1f, nameof(Syscall64.ConnectToNamedPort64) },
{ 0x21, nameof(Syscall64.SendSyncRequest64) }, { 0x21, nameof(Syscall64.SendSyncRequest64) },
{ 0x22, nameof(Syscall64.SendSyncRequestWithUserBuffer64) }, { 0x22, nameof(Syscall64.SendSyncRequestWithUserBuffer64) },
{ 0x24, nameof(Syscall64.GetProcessId64) }, { 0x23, nameof(Syscall64.SendAsyncRequestWithUserBuffer64) },
{ 0x25, nameof(Syscall64.GetThreadId64) }, { 0x24, nameof(Syscall64.GetProcessId64) },
{ 0x26, nameof(Syscall64.Break64) }, { 0x25, nameof(Syscall64.GetThreadId64) },
{ 0x27, nameof(Syscall64.OutputDebugString64) }, { 0x26, nameof(Syscall64.Break64) },
{ 0x29, nameof(Syscall64.GetInfo64) }, { 0x27, nameof(Syscall64.OutputDebugString64) },
{ 0x2c, nameof(Syscall64.MapPhysicalMemory64) }, { 0x29, nameof(Syscall64.GetInfo64) },
{ 0x2d, nameof(Syscall64.UnmapPhysicalMemory64) }, { 0x2c, nameof(Syscall64.MapPhysicalMemory64) },
{ 0x32, nameof(Syscall64.SetThreadActivity64) }, { 0x2d, nameof(Syscall64.UnmapPhysicalMemory64) },
{ 0x33, nameof(Syscall64.GetThreadContext364) }, { 0x32, nameof(Syscall64.SetThreadActivity64) },
{ 0x34, nameof(Syscall64.WaitForAddress64) }, { 0x33, nameof(Syscall64.GetThreadContext364) },
{ 0x35, nameof(Syscall64.SignalToAddress64) }, { 0x34, nameof(Syscall64.WaitForAddress64) },
{ 0x40, nameof(Syscall64.CreateSession64) }, { 0x35, nameof(Syscall64.SignalToAddress64) },
{ 0x41, nameof(Syscall64.AcceptSession64) }, { 0x40, nameof(Syscall64.CreateSession64) },
{ 0x43, nameof(Syscall64.ReplyAndReceive64) }, { 0x41, nameof(Syscall64.AcceptSession64) },
{ 0x45, nameof(Syscall64.CreateEvent64) }, { 0x43, nameof(Syscall64.ReplyAndReceive64) },
{ 0x65, nameof(Syscall64.GetProcessList64) }, { 0x44, nameof(Syscall64.ReplyAndReceiveWithUserBuffer64) },
{ 0x6f, nameof(Syscall64.GetSystemInfo64) }, { 0x45, nameof(Syscall64.CreateEvent64) },
{ 0x70, nameof(Syscall64.CreatePort64) }, { 0x65, nameof(Syscall64.GetProcessList64) },
{ 0x71, nameof(Syscall64.ManageNamedPort64) }, { 0x6f, nameof(Syscall64.GetSystemInfo64) },
{ 0x72, nameof(Syscall64.ConnectToPort64) }, { 0x70, nameof(Syscall64.CreatePort64) },
{ 0x73, nameof(Syscall64.SetProcessMemoryPermission64) }, { 0x71, nameof(Syscall64.ManageNamedPort64) },
{ 0x77, nameof(Syscall64.MapProcessCodeMemory64) }, { 0x72, nameof(Syscall64.ConnectToPort64) },
{ 0x78, nameof(Syscall64.UnmapProcessCodeMemory64) }, { 0x73, nameof(Syscall64.SetProcessMemoryPermission64) },
{ 0x7B, nameof(Syscall64.TerminateProcess64) } { 0x77, nameof(Syscall64.MapProcessCodeMemory64) },
{ 0x78, nameof(Syscall64.UnmapProcessCodeMemory64) },
{ 0x7B, nameof(Syscall64.TerminateProcess64) }
}; };
foreach (KeyValuePair<int, string> value in svcFuncs64) foreach (KeyValuePair<int, string> value in svcFuncs64)

View file

@ -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;