aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2020-07-17 01:19:07 -0300
committerGitHub <noreply@github.com>2020-07-17 14:19:07 +1000
commit9f6b24edfddf871320290463437b3f3cb7e29006 (patch)
tree4b8b45db5fe931ac37f843778c58a2d676fe3fba
parent46f8cef6a9e4a305803f1356446e25ce54909130 (diff)
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
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs11
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs76
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs34
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs47
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs7
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs8
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs6
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs325
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs262
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall32.cs6
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs34
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallTable.cs114
-rw-r--r--Ryujinx.Memory/MemoryBlock.cs4
14 files changed, 697 insertions, 239 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs
index 6aa211dd..0986adf7 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs
@@ -115,19 +115,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
ulong clientEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KMemoryManager.PageSize);
ulong clientEndAddrRounded = BitUtils.AlignUp (clientEndAddr, KMemoryManager.PageSize);
- ulong serverEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KMemoryManager.PageSize);
+ ulong serverEndAddrTruncated = BitUtils.AlignDown(serverEndAddr, KMemoryManager.PageSize);
- if (clientEndAddrTruncated < clientAddrRounded)
+ if (clientEndAddrTruncated < clientEndAddrRounded &&
+ (clientAddrTruncated == clientAddrRounded || clientAddrTruncated < clientEndAddrTruncated))
{
- KernelResult result = memoryManager.CopyDataToCurrentProcess(
+ KernelResult result = memoryManager.CopyDataFromCurrentProcess(
clientEndAddrTruncated,
clientEndAddr - clientEndAddrTruncated,
- serverEndAddrTruncated,
stateMask,
stateMask,
MemoryPermission.ReadAndWrite,
attributeMask,
- MemoryAttribute.None);
+ MemoryAttribute.None,
+ serverEndAddrTruncated);
if (result != KernelResult.Success)
{
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs
index 9c542ca0..8d6669cf 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs
@@ -1,21 +1,19 @@
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Services;
+using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
class KClientPort : KSynchronizationObject
{
private int _sessionsCount;
- private int _currentCapacity;
private readonly int _maxSessions;
private readonly KPort _parent;
public bool IsLight => _parent.IsLight;
- private readonly object _countIncLock;
-
// TODO: Remove that, we need it for now to allow HLE
// SM implementation to work with the new IPC system.
public IpcService Service { get; set; }
@@ -24,8 +22,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
_maxSessions = maxSessions;
_parent = parent;
-
- _countIncLock = new object();
}
public KernelResult Connect(out KClientSession clientSession)
@@ -40,26 +36,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return KernelResult.ResLimitExceeded;
}
- lock (_countIncLock)
+ if (!IncrementSessionsCount())
{
- if (_sessionsCount < _maxSessions)
- {
- _sessionsCount++;
- }
- else
- {
- currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
-
- return KernelResult.SessionCountExceeded;
- }
+ currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
- if (_currentCapacity < _sessionsCount)
- {
- _currentCapacity = _sessionsCount;
- }
+ return KernelResult.SessionCountExceeded;
}
- KSession session = new KSession(KernelContext);
+ KSession session = new KSession(KernelContext, this);
if (Service != null)
{
@@ -93,18 +77,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return KernelResult.ResLimitExceeded;
}
- lock (_countIncLock)
+ if (!IncrementSessionsCount())
{
- if (_sessionsCount < _maxSessions)
- {
- _sessionsCount++;
- }
- else
- {
- currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
+ currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
- return KernelResult.SessionCountExceeded;
- }
+ return KernelResult.SessionCountExceeded;
}
KLightSession session = new KLightSession(KernelContext);
@@ -124,6 +101,43 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return result;
}
+ private bool IncrementSessionsCount()
+ {
+ while (true)
+ {
+ int currentCount = _sessionsCount;
+
+ if (currentCount < _maxSessions)
+ {
+ if (Interlocked.CompareExchange(ref _sessionsCount, currentCount + 1, currentCount) == currentCount)
+ {
+ return true;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ public void Disconnect()
+ {
+ KernelContext.CriticalSection.Enter();
+
+ SignalIfMaximumReached(Interlocked.Decrement(ref _sessionsCount));
+
+ KernelContext.CriticalSection.Leave();
+ }
+
+ private void SignalIfMaximumReached(int value)
+ {
+ if (value == _maxSessions)
+ {
+ Signal();
+ }
+ }
+
public new static KernelResult RemoveName(KernelContext context, string name)
{
KAutoObject foundObj = FindNamedObject(context, name);
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
index b99dd1cb..262058d9 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
@@ -13,18 +13,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public ChannelState State { get; set; }
+ public KClientPort ParentPort { get; }
+
// TODO: Remove that, we need it for now to allow HLE
// services implementation to work with the new IPC system.
public IpcService Service { get; set; }
- public KClientSession(KernelContext context, KSession parent) : base(context)
+ public KClientSession(KernelContext context, KSession parent, KClientPort parentPort) : base(context)
{
- _parent = parent;
+ _parent = parent;
+ ParentPort = parentPort;
+
+ parentPort?.IncrementReferenceCount();
State = ChannelState.Open;
CreatorProcess = context.Scheduler.GetCurrentProcess();
-
CreatorProcess.IncrementReferenceCount();
}
@@ -51,6 +55,30 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return result;
}
+ public KernelResult SendAsyncRequest(KWritableEvent asyncEvent, ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
+ {
+ KThread currentThread = KernelContext.Scheduler.GetCurrentThread();
+
+ KSessionRequest request = new KSessionRequest(currentThread, customCmdBuffAddr, customCmdBuffSize, asyncEvent);
+
+ KernelContext.CriticalSection.Enter();
+
+ KernelResult result = _parent.ServerSession.EnqueueRequest(request);
+
+ KernelContext.CriticalSection.Leave();
+
+ return result;
+ }
+
+ public void DisconnectFromPort()
+ {
+ if (ParentPort != null)
+ {
+ ParentPort.Disconnect();
+ ParentPort.DecrementReferenceCount();
+ }
+ }
+
protected override void Destroy()
{
_parent.DisconnectClient();
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
index 70b54d05..48669832 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
@@ -633,7 +633,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
CloseAllHandles(clientMsg, serverHeader, clientProcess);
- CancelRequest(request, clientResult);
+ FinishRequest(request, clientResult);
}
if (clientHeader.ReceiveListType < 2 &&
@@ -770,6 +770,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
PointerBufferDesc descriptor = new PointerBufferDesc(pointerDesc);
+ ulong recvListBufferAddress = 0;
+
if (descriptor.BufferSize != 0)
{
clientResult = GetReceiveListAddress(
@@ -778,8 +780,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
clientHeader.ReceiveListType,
serverHeader.MessageSizeInWords,
receiveList,
- ref recvListDstOffset,
- out ulong recvListBufferAddress);
+ ref recvListDstOffset,
+ out recvListBufferAddress);
if (clientResult != KernelResult.Success)
{
@@ -806,6 +808,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
}
}
+ ulong dstDescAddress = clientMsg.DramAddress + offset * 4;
+
+ ulong clientPointerDesc =
+ (recvListBufferAddress << 32) |
+ ((recvListBufferAddress >> 20) & 0xf000) |
+ ((recvListBufferAddress >> 30) & 0xffc0);
+
+ clientPointerDesc |= pointerDesc & 0xffff000f;
+
+ KernelContext.Memory.Write(dstDescAddress + 0, clientPointerDesc);
+
offset += 2;
}
@@ -860,16 +873,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
}
// Unmap buffers from server.
- clientResult = request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager);
-
- if (clientResult != KernelResult.Success)
- {
- CleanUpForError();
-
- return serverResult;
- }
-
- WakeClientThread(request, clientResult);
+ FinishRequest(request, clientResult);
return serverResult;
}
@@ -1109,7 +1113,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
foreach (KSessionRequest request in IterateWithRemovalOfAllRequests())
{
- CancelRequest(request, KernelResult.PortRemoteClosed);
+ FinishRequest(request, KernelResult.PortRemoteClosed);
}
}
@@ -1180,7 +1184,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return hasRequest;
}
- private void CancelRequest(KSessionRequest request, KernelResult result)
+ private void FinishRequest(KSessionRequest request, KernelResult result)
{
KProcess clientProcess = request.ClientThread.Owner;
KProcess serverProcess = request.ServerProcess;
@@ -1221,14 +1225,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
KProcess clientProcess = request.ClientThread.Owner;
- ulong address = clientProcess.MemoryManager.GetDramAddressFromVa(request.CustomCmdBuffAddr);
+ if (result != KernelResult.Success)
+ {
+ ulong address = clientProcess.MemoryManager.GetDramAddressFromVa(request.CustomCmdBuffAddr);
- KernelContext.Memory.Write<ulong>(address, 0);
- KernelContext.Memory.Write(address + 8, (int)result);
+ KernelContext.Memory.Write<ulong>(address, 0);
+ KernelContext.Memory.Write(address + 8, (int)result);
+ }
- clientProcess.MemoryManager.UnborrowIpcBuffer(
- request.CustomCmdBuffAddr,
- request.CustomCmdBuffSize);
+ clientProcess.MemoryManager.UnborrowIpcBuffer(request.CustomCmdBuffAddr, request.CustomCmdBuffSize);
request.AsyncEvent.Signal();
}
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs
index 25e6eee5..4b5886a9 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs
@@ -11,10 +11,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
private bool _hasBeenInitialized;
- public KSession(KernelContext context) : base(context)
+ public KSession(KernelContext context, KClientPort parentPort = null) : base(context)
{
ServerSession = new KServerSession(context, this);
- ClientSession = new KClientSession(context, this);
+ ClientSession = new KClientSession(context, this, parentPort);
_hasBeenInitialized = true;
}
@@ -54,10 +54,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
if (_hasBeenInitialized)
{
+ ClientSession.DisconnectFromPort();
+
KProcess creatorProcess = ClientSession.CreatorProcess;
creatorProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
-
creatorProcess.DecrementReferenceCount();
}
}
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs
index f3467f39..31ddfc9c 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs
@@ -17,13 +17,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public ulong CustomCmdBuffSize { get; }
public KSessionRequest(
- KThread clientThread,
- ulong customCmdBuffAddr,
- ulong customCmdBuffSize)
+ KThread clientThread,
+ ulong customCmdBuffAddr,
+ ulong customCmdBuffSize,
+ KWritableEvent asyncEvent = null)
{
ClientThread = clientThread;
CustomCmdBuffAddr = customCmdBuffAddr;
CustomCmdBuffSize = customCmdBuffSize;
+ AsyncEvent = asyncEvent;
BufferDescriptorTable = new KBufferDescriptorTable();
}
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs
index b7c2b309..04e14e1b 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs
@@ -41,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
Attribute |= attribute;
}
- public void SetIpcMappingPermission(MemoryPermission permission)
+ public void SetIpcMappingPermission(MemoryPermission newPermission)
{
int oldIpcRefCount = IpcRefCount++;
@@ -52,10 +52,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
if (oldIpcRefCount == 0)
{
- SourcePermission = permission;
+ SourcePermission = Permission;
Permission &= ~MemoryPermission.ReadAndWrite;
- Permission |= MemoryPermission.ReadAndWrite & permission;
+ Permission |= MemoryPermission.ReadAndWrite & newPermission;
}
Attribute |= MemoryAttribute.IpcMapped;
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
index 1cbe4e7c..b13e2841 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
@@ -1688,6 +1688,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
}
ulong addressRounded = BitUtils.AlignUp (address, PageSize);
+ ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
@@ -1700,9 +1701,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
void CleanUpForError()
{
+ if (visitedSize == 0)
+ {
+ return;
+ }
+
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)
{
@@ -1729,42 +1735,45 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
KernelResult result;
- foreach (KMemoryInfo info in IterateOverRange(address, endAddrRounded))
+ if (addressRounded < endAddrTruncated)
{
- // Check if the block state matches what we expect.
- if ((info.State & stateMask) != stateMask ||
- (info.Permission & permission) != permission ||
- (info.Attribute & attributeMask) != MemoryAttribute.None)
+ foreach (KMemoryInfo info in IterateOverRange(addressTruncated, endAddrRounded))
{
- CleanUpForError();
-
- return KernelResult.InvalidMemState;
- }
+ // Check if the block state matches what we expect.
+ if ((info.State & stateMask) != stateMask ||
+ (info.Permission & permission) != permission ||
+ (info.Attribute & attributeMask) != MemoryAttribute.None)
+ {
+ CleanUpForError();
- ulong blockAddress = GetAddrInRange(info, addressRounded);
- ulong blockSize = GetSizeInRange(info, addressRounded, endAddrTruncated);
+ return KernelResult.InvalidMemState;
+ }
- ulong blockPagesCount = blockSize / PageSize;
+ ulong blockAddress = GetAddrInRange(info, addressRounded);
+ ulong blockSize = GetSizeInRange(info, addressRounded, endAddrTruncated);
- if ((info.Permission & MemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
- {
- result = DoMmuOperation(
- blockAddress,
- blockPagesCount,
- 0,
- false,
- permissionMask,
- MemoryOperation.ChangePermRw);
+ ulong blockPagesCount = blockSize / PageSize;
- if (result != KernelResult.Success)
+ if ((info.Permission & MemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
{
- CleanUpForError();
-
- return result;
+ result = DoMmuOperation(
+ blockAddress,
+ blockPagesCount,
+ 0,
+ false,
+ permissionMask,
+ MemoryOperation.ChangePermRw);
+
+ if (result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return result;
+ }
}
- }
- visitedSize += blockSize;
+ visitedSize += blockSize;
+ }
}
result = GetPagesForIpcTransfer(address, size, copyData, aslrDisabled, region, out pageList);
@@ -1778,7 +1787,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
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,
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;
+ KPageList pages = new KPageList();
+
ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
ulong addressRounded = BitUtils.AlignUp (address, PageSize);
ulong endAddr = address + size;
- ulong dstFirstPagePa = AllocateSinglePage(region, aslrDisabled);
-
- if (dstFirstPagePa == 0)
- {
- return KernelResult.OutOfMemory;
- }
-
- ulong dstLastPagePa = 0;
+ ulong dstFirstPagePa = 0;
+ ulong dstLastPagePa = 0;
void CleanUpForError()
{
- FreeSinglePage(region, dstFirstPagePa);
+ if (dstFirstPagePa != 0)
+ {
+ FreeSinglePage(region, dstFirstPagePa);
+ }
if (dstLastPagePa != 0)
{
@@ -1819,56 +1834,60 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
}
}
- ulong firstPageFillAddress = dstFirstPagePa;
-
- if (!ConvertVaToPa(addressTruncated, out ulong srcFirstPagePa))
+ // Is the first page address aligned?
+ // If not, allocate a new page and copy the unaligned chunck.
+ 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
- // 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)
- {
- ulong unusedSizeBefore = address - addressTruncated;
+ if (!TryConvertVaToPa(addressTruncated, out ulong srcFirstPagePa))
+ {
+ CleanUpForError();
- _context.Memory.ZeroFill(dstFirstPagePa, unusedSizeBefore);
+ return KernelResult.InvalidMemState;
+ }
- ulong copySize = addressRounded <= endAddr ? addressRounded - address : size;
+ ulong unusedSizeAfter;
- _context.Memory.Copy(
- GetDramAddressFromPa(dstFirstPagePa + unusedSizeBefore),
- GetDramAddressFromPa(srcFirstPagePa + unusedSizeBefore), copySize);
+ if (copyData)
+ {
+ ulong unusedSizeBefore = address - addressTruncated;
- firstPageFillAddress += unusedSizeBefore + copySize;
+ _context.Memory.ZeroFill(dstFirstPagePa, unusedSizeBefore);
- unusedSizeAfter = addressRounded > endAddr ? addressRounded - endAddr : 0;
- }
- else
- {
- unusedSizeAfter = PageSize;
- }
+ ulong copySize = addressRounded <= endAddr ? addressRounded - address : size;
- if (unusedSizeAfter != 0)
- {
- _context.Memory.ZeroFill(firstPageFillAddress, unusedSizeAfter);
- }
+ _context.Memory.Copy(
+ GetDramAddressFromPa(dstFirstPagePa + unusedSizeBefore),
+ GetDramAddressFromPa(srcFirstPagePa + unusedSizeBefore), copySize);
- KPageList pages = new KPageList();
+ firstPageFillAddress += unusedSizeBefore + copySize;
- if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success)
- {
- CleanUpForError();
+ unusedSizeAfter = addressRounded > endAddr ? addressRounded - endAddr : 0;
+ }
+ 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);
@@ -1881,9 +1900,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
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);
if (dstLastPagePa == 0)
@@ -1895,13 +1915,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong lastPageFillAddr = dstLastPagePa;
- if (!ConvertVaToPa(endAddrTruncated, out ulong srcLastPagePa))
+ if (!TryConvertVaToPa(endAddrTruncated, out ulong srcLastPagePa))
{
CleanUpForError();
return KernelResult.InvalidMemState;
}
+ ulong unusedSizeAfter;
+
if (copyData)
{
ulong copySize = endAddr - endAddrTruncated;
@@ -1921,7 +1943,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
_context.Memory.ZeroFill(lastPageFillAddr, unusedSizeAfter);
- if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success)
+ if (pages.AddRange(dstLastPagePa, 1) != KernelResult.Success)
{
CleanUpForError();
@@ -1954,9 +1976,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
MemoryPermission permission,
MemoryState state,
KPageList pageList,
- out ulong mappedVa)
+ out ulong dst)
{
- mappedVa = 0;
+ dst = 0;
lock (_blocks)
{
@@ -2002,7 +2024,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
InsertBlock(va, neededPagesCount, state, permission);
- mappedVa = va;
+ dst = va + (address - addressTruncated);
}
return KernelResult.Success;
@@ -2044,6 +2066,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
}
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 pagesCount = (endAddrRounded - addressTruncated) / PageSize;
@@ -2056,6 +2080,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
MemoryPermission.None,
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)
{
InsertBlock(addressTruncated, pagesCount, MemoryState.Unmapped);
@@ -2107,7 +2143,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
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.
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;
}
+ 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)
{
return ClearAttributesAndChangePermission(
@@ -2172,6 +2310,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
MemoryAttribute attributeClearMask,
KPageList pageList = null)
{
+ if (address + size <= address || !InsideAddrSpace(address, size))
+ {
+ return KernelResult.InvalidMemState;
+ }
+
lock (_blocks)
{
if (CheckRange(
@@ -2247,7 +2390,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
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.");
}
@@ -3114,7 +3257,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
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);
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
index c67e5c5c..0fa22837 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
@@ -544,7 +544,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
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.");
}
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
index a0a15fcf..fba22fc1 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
@@ -1,5 +1,4 @@
-using ARMeilleure.Memory;
-using Ryujinx.Common;
+using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.Exceptions;
@@ -96,16 +95,25 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
- public KernelResult SendSyncRequest(int handle)
+ public KernelResult SendSyncRequestHLE(int handle)
{
- return SendSyncRequestWithUserBuffer((ulong)_context.Scheduler.GetCurrentThread().Context.Tpidr, 0x100, handle);
+ KProcess process = _context.Scheduler.GetCurrentProcess();
+
+ KClientSession clientSession = process.HandleTable.GetObject<KClientSession>(handle);
+
+ if (clientSession == null || clientSession.Service == null)
+ {
+ return SendSyncRequest(handle);
+ }
+
+ return SendSyncRequestWithUserBufferHLE((ulong)_context.Scheduler.GetCurrentThread().Context.Tpidr, 0x100, handle);
}
- public KernelResult SendSyncRequestWithUserBuffer(ulong messagePtr, ulong size, int handle)
+ public KernelResult SendSyncRequestWithUserBufferHLE(ulong messagePtr, ulong messageSize, int handle)
{
KProcess process = _context.Scheduler.GetCurrentProcess();
- byte[] messageData = new byte[size];
+ byte[] messageData = new byte[messageSize];
process.CpuMemory.Read(messagePtr, messageData);
@@ -113,7 +121,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
if (clientSession == null || clientSession.Service == null)
{
- return SendSyncRequest_(handle);
+ return SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle);
}
if (clientSession != null)
@@ -168,7 +176,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
ipcMessage.Thread.Reschedule(ThreadSchedState.Running);
}
- private KernelResult SendSyncRequest_(int handle)
+ private KernelResult SendSyncRequest(int handle)
{
KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
@@ -182,6 +190,123 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
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(
bool isLight,
ulong namePtr,
@@ -348,7 +473,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
syncObjs[index] = obj;
}
- KernelResult result;
+ KernelResult result = KernelResult.Success;
if (replyTargetHandle != 0)
{
@@ -356,32 +481,131 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
if (replyTarget == null)
{
- return KernelResult.InvalidHandle;
+ result = KernelResult.InvalidHandle;
}
+ else
+ {
+ result = replyTarget.Reply();
+ }
+ }
- 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 (result != KernelResult.Success)
+ 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)
{
- return result;
+ currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize);
+
+ return KernelResult.InvalidHandle;
}
+
+ syncObjs[index] = obj;
}
- 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
+ {
+ result = replyTarget.Reply(messagePtr, messageSize);
}
+ }
- if ((result = session.Receive()) != KernelResult.NotFound)
+ if (result == KernelResult.Success)
+ {
+ while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == KernelResult.Success)
{
- break;
+ 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;
}
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall32.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall32.cs
index d7cbcbf5..224af6d8 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall32.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall32.cs
@@ -22,12 +22,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
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(
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs
index 5dfcdcba..47f78a25 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs
@@ -22,12 +22,21 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
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(
@@ -54,6 +63,25 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
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(
[R(2)] int maxSessions,
[R(3)] bool isLight,
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallTable.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallTable.cs
index 043a54af..dcfd9347 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallTable.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallTable.cs
@@ -24,62 +24,64 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
Dictionary<int, string> svcFuncs64 = new Dictionary<int, string>
{
- { 0x01, nameof(Syscall64.SetHeapSize64) },
- { 0x03, nameof(Syscall64.SetMemoryAttribute64) },
- { 0x04, nameof(Syscall64.MapMemory64) },
- { 0x05, nameof(Syscall64.UnmapMemory64) },
- { 0x06, nameof(Syscall64.QueryMemory64) },
- { 0x07, nameof(Syscall64.ExitProcess64) },
- { 0x08, nameof(Syscall64.CreateThread64) },
- { 0x09, nameof(Syscall64.StartThread64) },
- { 0x0a, nameof(Syscall64.ExitThread64) },
- { 0x0b, nameof(Syscall64.SleepThread64) },
- { 0x0c, nameof(Syscall64.GetThreadPriority64) },
- { 0x0d, nameof(Syscall64.SetThreadPriority64) },
- { 0x0e, nameof(Syscall64.GetThreadCoreMask64) },
- { 0x0f, nameof(Syscall64.SetThreadCoreMask64) },
- { 0x10, nameof(Syscall64.GetCurrentProcessorNumber64) },
- { 0x11, nameof(Syscall64.SignalEvent64) },
- { 0x12, nameof(Syscall64.ClearEvent64) },
- { 0x13, nameof(Syscall64.MapSharedMemory64) },
- { 0x14, nameof(Syscall64.UnmapSharedMemory64) },
- { 0x15, nameof(Syscall64.CreateTransferMemory64) },
- { 0x16, nameof(Syscall64.CloseHandle64) },
- { 0x17, nameof(Syscall64.ResetSignal64) },
- { 0x18, nameof(Syscall64.WaitSynchronization64) },
- { 0x19, nameof(Syscall64.CancelSynchronization64) },
- { 0x1a, nameof(Syscall64.ArbitrateLock64) },
- { 0x1b, nameof(Syscall64.ArbitrateUnlock64) },
- { 0x1c, nameof(Syscall64.WaitProcessWideKeyAtomic64) },
- { 0x1d, nameof(Syscall64.SignalProcessWideKey64) },
- { 0x1e, nameof(Syscall64.GetSystemTick64) },
- { 0x1f, nameof(Syscall64.ConnectToNamedPort64) },
- { 0x21, nameof(Syscall64.SendSyncRequest64) },
- { 0x22, nameof(Syscall64.SendSyncRequestWithUserBuffer64) },
- { 0x24, nameof(Syscall64.GetProcessId64) },
- { 0x25, nameof(Syscall64.GetThreadId64) },
- { 0x26, nameof(Syscall64.Break64) },
- { 0x27, nameof(Syscall64.OutputDebugString64) },
- { 0x29, nameof(Syscall64.GetInfo64) },
- { 0x2c, nameof(Syscall64.MapPhysicalMemory64) },
- { 0x2d, nameof(Syscall64.UnmapPhysicalMemory64) },
- { 0x32, nameof(Syscall64.SetThreadActivity64) },
- { 0x33, nameof(Syscall64.GetThreadContext364) },
- { 0x34, nameof(Syscall64.WaitForAddress64) },
- { 0x35, nameof(Syscall64.SignalToAddress64) },
- { 0x40, nameof(Syscall64.CreateSession64) },
- { 0x41, nameof(Syscall64.AcceptSession64) },
- { 0x43, nameof(Syscall64.ReplyAndReceive64) },
- { 0x45, nameof(Syscall64.CreateEvent64) },
- { 0x65, nameof(Syscall64.GetProcessList64) },
- { 0x6f, nameof(Syscall64.GetSystemInfo64) },
- { 0x70, nameof(Syscall64.CreatePort64) },
- { 0x71, nameof(Syscall64.ManageNamedPort64) },
- { 0x72, nameof(Syscall64.ConnectToPort64) },
- { 0x73, nameof(Syscall64.SetProcessMemoryPermission64) },
- { 0x77, nameof(Syscall64.MapProcessCodeMemory64) },
- { 0x78, nameof(Syscall64.UnmapProcessCodeMemory64) },
- { 0x7B, nameof(Syscall64.TerminateProcess64) }
+ { 0x01, nameof(Syscall64.SetHeapSize64) },
+ { 0x03, nameof(Syscall64.SetMemoryAttribute64) },
+ { 0x04, nameof(Syscall64.MapMemory64) },
+ { 0x05, nameof(Syscall64.UnmapMemory64) },
+ { 0x06, nameof(Syscall64.QueryMemory64) },
+ { 0x07, nameof(Syscall64.ExitProcess64) },
+ { 0x08, nameof(Syscall64.CreateThread64) },
+ { 0x09, nameof(Syscall64.StartThread64) },
+ { 0x0a, nameof(Syscall64.ExitThread64) },
+ { 0x0b, nameof(Syscall64.SleepThread64) },
+ { 0x0c, nameof(Syscall64.GetThreadPriority64) },
+ { 0x0d, nameof(Syscall64.SetThreadPriority64) },
+ { 0x0e, nameof(Syscall64.GetThreadCoreMask64) },
+ { 0x0f, nameof(Syscall64.SetThreadCoreMask64) },
+ { 0x10, nameof(Syscall64.GetCurrentProcessorNumber64) },
+ { 0x11, nameof(Syscall64.SignalEvent64) },
+ { 0x12, nameof(Syscall64.ClearEvent64) },
+ { 0x13, nameof(Syscall64.MapSharedMemory64) },
+ { 0x14, nameof(Syscall64.UnmapSharedMemory64) },
+ { 0x15, nameof(Syscall64.CreateTransferMemory64) },
+ { 0x16, nameof(Syscall64.CloseHandle64) },
+ { 0x17, nameof(Syscall64.ResetSignal64) },
+ { 0x18, nameof(Syscall64.WaitSynchronization64) },
+ { 0x19, nameof(Syscall64.CancelSynchronization64) },
+ { 0x1a, nameof(Syscall64.ArbitrateLock64) },
+ { 0x1b, nameof(Syscall64.ArbitrateUnlock64) },
+ { 0x1c, nameof(Syscall64.WaitProcessWideKeyAtomic64) },
+ { 0x1d, nameof(Syscall64.SignalProcessWideKey64) },
+ { 0x1e, nameof(Syscall64.GetSystemTick64) },
+ { 0x1f, nameof(Syscall64.ConnectToNamedPort64) },
+ { 0x21, nameof(Syscall64.SendSyncRequest64) },
+ { 0x22, nameof(Syscall64.SendSyncRequestWithUserBuffer64) },
+ { 0x23, nameof(Syscall64.SendAsyncRequestWithUserBuffer64) },
+ { 0x24, nameof(Syscall64.GetProcessId64) },
+ { 0x25, nameof(Syscall64.GetThreadId64) },
+ { 0x26, nameof(Syscall64.Break64) },
+ { 0x27, nameof(Syscall64.OutputDebugString64) },
+ { 0x29, nameof(Syscall64.GetInfo64) },
+ { 0x2c, nameof(Syscall64.MapPhysicalMemory64) },
+ { 0x2d, nameof(Syscall64.UnmapPhysicalMemory64) },
+ { 0x32, nameof(Syscall64.SetThreadActivity64) },
+ { 0x33, nameof(Syscall64.GetThreadContext364) },
+ { 0x34, nameof(Syscall64.WaitForAddress64) },
+ { 0x35, nameof(Syscall64.SignalToAddress64) },
+ { 0x40, nameof(Syscall64.CreateSession64) },
+ { 0x41, nameof(Syscall64.AcceptSession64) },
+ { 0x43, nameof(Syscall64.ReplyAndReceive64) },
+ { 0x44, nameof(Syscall64.ReplyAndReceiveWithUserBuffer64) },
+ { 0x45, nameof(Syscall64.CreateEvent64) },
+ { 0x65, nameof(Syscall64.GetProcessList64) },
+ { 0x6f, nameof(Syscall64.GetSystemInfo64) },
+ { 0x70, nameof(Syscall64.CreatePort64) },
+ { 0x71, nameof(Syscall64.ManageNamedPort64) },
+ { 0x72, nameof(Syscall64.ConnectToPort64) },
+ { 0x73, nameof(Syscall64.SetProcessMemoryPermission64) },
+ { 0x77, nameof(Syscall64.MapProcessCodeMemory64) },
+ { 0x78, nameof(Syscall64.UnmapProcessCodeMemory64) },
+ { 0x7B, nameof(Syscall64.TerminateProcess64) }
};
foreach (KeyValuePair<int, string> value in svcFuncs64)
diff --git a/Ryujinx.Memory/MemoryBlock.cs b/Ryujinx.Memory/MemoryBlock.cs
index 37439a8a..065e0713 100644
--- a/Ryujinx.Memory/MemoryBlock.cs
+++ b/Ryujinx.Memory/MemoryBlock.cs
@@ -127,12 +127,12 @@ namespace Ryujinx.Memory
/// <summary>
/// Copies data from one memory location to another.
/// </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="srcOffset">Source offset to read the data from</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="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;