aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.HLE/DeviceMemory.cs57
-rw-r--r--Ryujinx.HLE/HOS/Horizon.cs15
-rw-r--r--Ryujinx.HLE/HOS/Ipc/IpcHandler.cs12
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs21
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs14
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Common/KernelResult.cs57
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs20
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/ChannelState.cs10
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptor.cs20
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs216
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs110
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs60
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KLightClientSession.cs14
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KLightServerSession.cs14
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs20
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KPort.cs65
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KServerPort.cs75
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs1262
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs50
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs31
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs106
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs31
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs1270
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs285
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs5
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs6
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Process/KHandleEntry.cs6
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs89
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs20
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs5
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs23
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs532
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs4
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs181
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs6
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs30
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs20
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs62
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs4
-rw-r--r--Ryujinx.HLE/HOS/ProgramLoader.cs21
-rw-r--r--Ryujinx.HLE/HOS/ServiceCtx.cs32
-rw-r--r--Ryujinx.HLE/HOS/Services/IpcService.cs10
-rw-r--r--Ryujinx.HLE/HOS/Services/Psm/IPsmSession.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs106
-rw-r--r--Ryujinx.HLE/HOS/Services/Sm/SmErr.cs9
-rw-r--r--Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs60
-rw-r--r--Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs10
-rw-r--r--Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs2
51 files changed, 4278 insertions, 808 deletions
diff --git a/Ryujinx.HLE/DeviceMemory.cs b/Ryujinx.HLE/DeviceMemory.cs
index 3db89c72..310942b8 100644
--- a/Ryujinx.HLE/DeviceMemory.cs
+++ b/Ryujinx.HLE/DeviceMemory.cs
@@ -113,6 +113,63 @@ namespace Ryujinx.HLE
}
}
+ public void Set(ulong address, byte value, ulong size)
+ {
+ if (address + size < address)
+ {
+ throw new ArgumentOutOfRangeException(nameof(size));
+ }
+
+ if (address + size > RamSize)
+ {
+ throw new ArgumentOutOfRangeException(nameof(address));
+ }
+
+ ulong size8 = size & ~7UL;
+
+ ulong valueRep = (ulong)value * 0x0101010101010101;
+
+ for (ulong offs = 0; offs < size8; offs += 8)
+ {
+ WriteUInt64((long)(address + offs), valueRep);
+ }
+
+ for (ulong offs = size8; offs < (size - size8); offs++)
+ {
+ WriteByte((long)(address + offs), value);
+ }
+ }
+
+ public void Copy(ulong dst, ulong src, ulong size)
+ {
+ if (dst + size < dst || src + size < src)
+ {
+ throw new ArgumentOutOfRangeException(nameof(size));
+ }
+
+ if (dst + size > RamSize)
+ {
+ throw new ArgumentOutOfRangeException(nameof(dst));
+ }
+
+ if (src + size > RamSize)
+ {
+ throw new ArgumentOutOfRangeException(nameof(src));
+ }
+
+ ulong size8 = size & ~7UL;
+
+ for (ulong offs = 0; offs < size8; offs += 8)
+ {
+ WriteUInt64((long)(dst + offs), ReadUInt64((long)(src + offs)));
+ }
+
+ for (ulong offs = size8; offs < (size - size8); offs++)
+ {
+ WriteByte((long)(dst + offs), ReadByte((long)(src + offs)));
+ }
+ }
+
public void Dispose()
{
Dispose(true);
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index 94b2e6c6..6b5f5723 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -7,6 +7,7 @@ using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.HLE.HOS.Services.Sm;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Loaders.Npdm;
@@ -157,8 +158,8 @@ namespace Ryujinx.HLE.HOS
hidPageList .AddRange(hidPa, HidSize / KMemoryManager.PageSize);
fontPageList.AddRange(fontPa, FontSize / KMemoryManager.PageSize);
- HidSharedMem = new KSharedMemory(hidPageList, 0, 0, MemoryPermission.Read);
- FontSharedMem = new KSharedMemory(fontPageList, 0, 0, MemoryPermission.Read);
+ HidSharedMem = new KSharedMemory(this, hidPageList, 0, 0, MemoryPermission.Read);
+ FontSharedMem = new KSharedMemory(this, fontPageList, 0, 0, MemoryPermission.Read);
AppletState = new AppletStateMgr(this);
@@ -166,6 +167,8 @@ namespace Ryujinx.HLE.HOS
Font = new SharedFontManager(device, (long)(fontPa - DramMemoryMap.DramBase));
+ IUserInterface.InitializePort(this);
+
VsyncEvent = new KEvent(this);
LoadKeySet();
@@ -259,6 +262,14 @@ namespace Ryujinx.HLE.HOS
LoadNca(mainNca, controlNca);
}
+ public void LoadKip(string kipFile)
+ {
+ using (FileStream fs = new FileStream(kipFile, FileMode.Open))
+ {
+ ProgramLoader.LoadKernelInitalProcess(this, new KernelInitialProcess(fs));
+ }
+ }
+
private (Nca Main, Nca Control) GetXciGameData(Xci xci)
{
if (xci.SecurePartition == null)
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs b/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs
index ecfa25ed..1eba4b41 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs
@@ -10,12 +10,12 @@ namespace Ryujinx.HLE.HOS.Ipc
static class IpcHandler
{
public static KernelResult IpcCall(
- Switch device,
- KProcess process,
- MemoryManager memory,
- KSession session,
- IpcMessage request,
- long cmdPtr)
+ Switch device,
+ KProcess process,
+ MemoryManager memory,
+ KClientSession session,
+ IpcMessage request,
+ long cmdPtr)
{
IpcMessage response = new IpcMessage();
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs b/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs
index ddb0c71f..3b30dd80 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs
@@ -1,12 +1,18 @@
+using System.Threading;
+
namespace Ryujinx.HLE.HOS.Kernel.Common
{
class KAutoObject
{
protected Horizon System;
+ private int _referenceCount;
+
public KAutoObject(Horizon system)
{
System = system;
+
+ _referenceCount = 1;
}
public virtual KernelResult SetName(string name)
@@ -38,5 +44,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
return null;
}
+
+ public void IncrementReferenceCount()
+ {
+ Interlocked.Increment(ref _referenceCount);
+ }
+
+ public void DecrementReferenceCount()
+ {
+ if (Interlocked.Decrement(ref _referenceCount) == 0)
+ {
+ Destroy();
+ }
+ }
+
+ protected virtual void Destroy() { }
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs b/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs
index 01bba65f..a7955d7a 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs
@@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Kernel.Common
{
- class KResourceLimit
+ class KResourceLimit : KAutoObject
{
private const int Time10SecondsMs = 10000;
@@ -18,9 +18,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
private int _waitingThreadsCount;
- private Horizon _system;
-
- public KResourceLimit(Horizon system)
+ public KResourceLimit(Horizon system) : base(system)
{
_current = new long[(int)LimitableResource.Count];
_limit = new long[(int)LimitableResource.Count];
@@ -29,8 +27,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
_lockObj = new object();
_waitingThreads = new LinkedList<KThread>();
-
- _system = system;
}
public bool Reserve(LimitableResource resource, ulong amount)
@@ -61,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{
_waitingThreadsCount++;
- KConditionVariable.Wait(_system, _waitingThreads, _lockObj, timeout);
+ KConditionVariable.Wait(System, _waitingThreads, _lockObj, timeout);
_waitingThreadsCount--;
@@ -94,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
Release(resource, amount, amount);
}
- private void Release(LimitableResource resource, long usedAmount, long availableAmount)
+ public void Release(LimitableResource resource, long usedAmount, long availableAmount)
{
int index = GetIndex(resource);
@@ -105,7 +101,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
if (_waitingThreadsCount > 0)
{
- KConditionVariable.NotifyAll(_system, _waitingThreads);
+ KConditionVariable.NotifyAll(System, _waitingThreads);
}
}
}
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs b/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs
index 87e55312..77d8fbff 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs
@@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{
class KSynchronizationObject : KAutoObject
{
- public LinkedList<KThread> WaitingThreads;
+ public LinkedList<KThread> WaitingThreads { get; }
public KSynchronizationObject(Horizon system) : base(system)
{
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KernelResult.cs b/Ryujinx.HLE/HOS/Kernel/Common/KernelResult.cs
index cea24693..357b01ea 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/KernelResult.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KernelResult.cs
@@ -2,31 +2,36 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{
enum KernelResult
{
- Success = 0,
- InvalidCapability = 0x1c01,
- ThreadNotStarted = 0x7201,
- ThreadTerminating = 0x7601,
- InvalidSize = 0xca01,
- InvalidAddress = 0xcc01,
- OutOfResource = 0xce01,
- OutOfMemory = 0xd001,
- HandleTableFull = 0xd201,
- InvalidMemState = 0xd401,
- InvalidPermission = 0xd801,
- InvalidMemRange = 0xdc01,
- InvalidPriority = 0xe001,
- InvalidCpuCore = 0xe201,
- InvalidHandle = 0xe401,
- UserCopyFailed = 0xe601,
- InvalidCombination = 0xe801,
- TimedOut = 0xea01,
- Cancelled = 0xec01,
- MaximumExceeded = 0xee01,
- InvalidEnumValue = 0xf001,
- NotFound = 0xf201,
- InvalidThread = 0xf401,
- InvalidState = 0xfa01,
- ReservedValue = 0xfc01,
- ResLimitExceeded = 0x10801
+ Success = 0,
+ SessionCountExceeded = 0xe01,
+ InvalidCapability = 0x1c01,
+ ThreadNotStarted = 0x7201,
+ ThreadTerminating = 0x7601,
+ InvalidSize = 0xca01,
+ InvalidAddress = 0xcc01,
+ OutOfResource = 0xce01,
+ OutOfMemory = 0xd001,
+ HandleTableFull = 0xd201,
+ InvalidMemState = 0xd401,
+ InvalidPermission = 0xd801,
+ InvalidMemRange = 0xdc01,
+ InvalidPriority = 0xe001,
+ InvalidCpuCore = 0xe201,
+ InvalidHandle = 0xe401,
+ UserCopyFailed = 0xe601,
+ InvalidCombination = 0xe801,
+ TimedOut = 0xea01,
+ Cancelled = 0xec01,
+ MaximumExceeded = 0xee01,
+ InvalidEnumValue = 0xf001,
+ NotFound = 0xf201,
+ InvalidThread = 0xf401,
+ PortRemoteClosed = 0xf601,
+ InvalidState = 0xfa01,
+ ReservedValue = 0xfc01,
+ PortClosed = 0x10601,
+ ResLimitExceeded = 0x10801,
+ OutOfVaSpace = 0x20601,
+ CmdBufferTooSmall = 0x20801
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs b/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
index a29b1722..2b759140 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
@@ -22,6 +22,26 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
return false;
}
+ public static bool UserToKernelInt32Array(Horizon system, ulong address, int[] values)
+ {
+ KProcess currentProcess = system.Scheduler.GetCurrentProcess();
+
+ for (int index = 0; index < values.Length; index++, address += 4)
+ {
+ if (currentProcess.CpuMemory.IsMapped((long)address) &&
+ currentProcess.CpuMemory.IsMapped((long)address + 3))
+ {
+ values[index]= currentProcess.CpuMemory.ReadInt32((long)address);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
public static bool UserToKernelString(Horizon system, ulong address, int size, out string value)
{
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/ChannelState.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/ChannelState.cs
new file mode 100644
index 00000000..4827384e
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/ChannelState.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.HLE.HOS.Kernel.Ipc
+{
+ enum ChannelState
+ {
+ NotInitialized,
+ Open,
+ ClientDisconnected,
+ ServerDisconnected
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptor.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptor.cs
new file mode 100644
index 00000000..e28244d4
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptor.cs
@@ -0,0 +1,20 @@
+using Ryujinx.HLE.HOS.Kernel.Memory;
+
+namespace Ryujinx.HLE.HOS.Kernel.Ipc
+{
+ class KBufferDescriptor
+ {
+ public ulong ClientAddress { get; }
+ public ulong ServerAddress { get; }
+ public ulong Size { get; }
+ public MemoryState State { get; }
+
+ public KBufferDescriptor(ulong src, ulong dst, ulong size, MemoryState state)
+ {
+ ClientAddress = src;
+ ServerAddress = dst;
+ Size = size;
+ State = state;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs
new file mode 100644
index 00000000..ac805bee
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs
@@ -0,0 +1,216 @@
+using Ryujinx.Common;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Memory;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Kernel.Ipc
+{
+ class KBufferDescriptorTable
+ {
+ private const int MaxInternalBuffersCount = 8;
+
+ private List<KBufferDescriptor> _sendBufferDescriptors;
+ private List<KBufferDescriptor> _receiveBufferDescriptors;
+ private List<KBufferDescriptor> _exchangeBufferDescriptors;
+
+ public KBufferDescriptorTable()
+ {
+ _sendBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount);
+ _receiveBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount);
+ _exchangeBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount);
+ }
+
+ public KernelResult AddSendBuffer(ulong src, ulong dst, ulong size, MemoryState state)
+ {
+ return Add(_sendBufferDescriptors, src, dst, size, state);
+ }
+
+ public KernelResult AddReceiveBuffer(ulong src, ulong dst, ulong size, MemoryState state)
+ {
+ return Add(_receiveBufferDescriptors, src, dst, size, state);
+ }
+
+ public KernelResult AddExchangeBuffer(ulong src, ulong dst, ulong size, MemoryState state)
+ {
+ return Add(_exchangeBufferDescriptors, src, dst, size, state);
+ }
+
+ private KernelResult Add(List<KBufferDescriptor> list, ulong src, ulong dst, ulong size, MemoryState state)
+ {
+ if (list.Count < MaxInternalBuffersCount)
+ {
+ list.Add(new KBufferDescriptor(src, dst, size, state));
+
+ return KernelResult.Success;
+ }
+
+ return KernelResult.OutOfMemory;
+ }
+
+ public KernelResult CopyBuffersToClient(KMemoryManager memoryManager)
+ {
+ KernelResult result = CopyToClient(memoryManager, _receiveBufferDescriptors);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ return CopyToClient(memoryManager, _exchangeBufferDescriptors);
+ }
+
+ private KernelResult CopyToClient(KMemoryManager memoryManager, List<KBufferDescriptor> list)
+ {
+ foreach (KBufferDescriptor desc in list)
+ {
+ MemoryState stateMask;
+
+ switch (desc.State)
+ {
+ case MemoryState.IpcBuffer0: stateMask = MemoryState.IpcSendAllowedType0; break;
+ case MemoryState.IpcBuffer1: stateMask = MemoryState.IpcSendAllowedType1; break;
+ case MemoryState.IpcBuffer3: stateMask = MemoryState.IpcSendAllowedType3; break;
+
+ default: return KernelResult.InvalidCombination;
+ }
+
+ MemoryAttribute attributeMask = MemoryAttribute.Borrowed | MemoryAttribute.Uncached;
+
+ if (desc.State == MemoryState.IpcBuffer0)
+ {
+ attributeMask |= MemoryAttribute.DeviceMapped;
+ }
+
+ ulong clientAddrTruncated = BitUtils.AlignDown(desc.ClientAddress, KMemoryManager.PageSize);
+ ulong clientAddrRounded = BitUtils.AlignUp (desc.ClientAddress, KMemoryManager.PageSize);
+
+ //Check if address is not aligned, in this case we need to perform 2 copies.
+ if (clientAddrTruncated != clientAddrRounded)
+ {
+ ulong copySize = clientAddrRounded - desc.ClientAddress;
+
+ if (copySize > desc.Size)
+ {
+ copySize = desc.Size;
+ }
+
+ KernelResult result = memoryManager.CopyDataFromCurrentProcess(
+ desc.ClientAddress,
+ copySize,
+ stateMask,
+ stateMask,
+ MemoryPermission.ReadAndWrite,
+ attributeMask,
+ MemoryAttribute.None,
+ desc.ServerAddress);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+ }
+
+ ulong clientEndAddr = desc.ClientAddress + desc.Size;
+ ulong serverEndAddr = desc.ServerAddress + desc.Size;
+
+ ulong clientEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KMemoryManager.PageSize);
+ ulong clientEndAddrRounded = BitUtils.AlignUp (clientEndAddr, KMemoryManager.PageSize);
+ ulong serverEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KMemoryManager.PageSize);
+
+ if (clientEndAddrTruncated < clientAddrRounded)
+ {
+ KernelResult result = memoryManager.CopyDataToCurrentProcess(
+ clientEndAddrTruncated,
+ clientEndAddr - clientEndAddrTruncated,
+ serverEndAddrTruncated,
+ stateMask,
+ stateMask,
+ MemoryPermission.ReadAndWrite,
+ attributeMask,
+ MemoryAttribute.None);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+ }
+ }
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult UnmapServerBuffers(KMemoryManager memoryManager)
+ {
+ KernelResult result = UnmapServer(memoryManager, _sendBufferDescriptors);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ result = UnmapServer(memoryManager, _receiveBufferDescriptors);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ return UnmapServer(memoryManager, _exchangeBufferDescriptors);
+ }
+
+ private KernelResult UnmapServer(KMemoryManager memoryManager, List<KBufferDescriptor> list)
+ {
+ foreach (KBufferDescriptor descriptor in list)
+ {
+ KernelResult result = memoryManager.UnmapNoAttributeIfStateEquals(
+ descriptor.ServerAddress,
+ descriptor.Size,
+ descriptor.State);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+ }
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult RestoreClientBuffers(KMemoryManager memoryManager)
+ {
+ KernelResult result = RestoreClient(memoryManager, _sendBufferDescriptors);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ result = RestoreClient(memoryManager, _receiveBufferDescriptors);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ return RestoreClient(memoryManager, _exchangeBufferDescriptors);
+ }
+
+ private KernelResult RestoreClient(KMemoryManager memoryManager, List<KBufferDescriptor> list)
+ {
+ foreach (KBufferDescriptor descriptor in list)
+ {
+ KernelResult result = memoryManager.UnmapIpcRestorePermission(
+ descriptor.ClientAddress,
+ descriptor.Size,
+ descriptor.State);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+ }
+
+ return KernelResult.Success;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs
index ddfe2096..eaa4322d 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs
@@ -1,4 +1,6 @@
using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.HLE.HOS.Services;
namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
@@ -10,12 +12,116 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
private KPort _parent;
- public KClientPort(Horizon system) : base(system) { }
+ public bool IsLight => _parent.IsLight;
- public void Initialize(KPort parent, int maxSessions)
+ private 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; }
+
+ public KClientPort(Horizon system, KPort parent, int maxSessions) : base(system)
{
_maxSessions = maxSessions;
_parent = parent;
+
+ _countIncLock = new object();
+ }
+
+ public KernelResult Connect(out KClientSession clientSession)
+ {
+ clientSession = null;
+
+ KProcess currentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (currentProcess.ResourceLimit != null &&
+ !currentProcess.ResourceLimit.Reserve(LimitableResource.Session, 1))
+ {
+ return KernelResult.ResLimitExceeded;
+ }
+
+ lock (_countIncLock)
+ {
+ if (_sessionsCount < _maxSessions)
+ {
+ _sessionsCount++;
+ }
+ else
+ {
+ currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
+
+ return KernelResult.SessionCountExceeded;
+ }
+
+ if (_currentCapacity < _sessionsCount)
+ {
+ _currentCapacity = _sessionsCount;
+ }
+ }
+
+ KSession session = new KSession(System);
+
+ if (Service != null)
+ {
+ session.ClientSession.Service = Service;
+ }
+
+ KernelResult result = _parent.EnqueueIncomingSession(session.ServerSession);
+
+ if (result != KernelResult.Success)
+ {
+ session.ClientSession.DecrementReferenceCount();
+ session.ServerSession.DecrementReferenceCount();
+
+ return result;
+ }
+
+ clientSession = session.ClientSession;
+
+ return result;
+ }
+
+ public KernelResult ConnectLight(out KLightClientSession clientSession)
+ {
+ clientSession = null;
+
+ KProcess currentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (currentProcess.ResourceLimit != null &&
+ !currentProcess.ResourceLimit.Reserve(LimitableResource.Session, 1))
+ {
+ return KernelResult.ResLimitExceeded;
+ }
+
+ lock (_countIncLock)
+ {
+ if (_sessionsCount < _maxSessions)
+ {
+ _sessionsCount++;
+ }
+ else
+ {
+ currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
+
+ return KernelResult.SessionCountExceeded;
+ }
+ }
+
+ KLightSession session = new KLightSession(System);
+
+ KernelResult result = _parent.EnqueueIncomingLightSession(session.ServerSession);
+
+ if (result != KernelResult.Success)
+ {
+ session.ClientSession.DecrementReferenceCount();
+ session.ServerSession.DecrementReferenceCount();
+
+ return result;
+ }
+
+ clientSession = session.ClientSession;
+
+ return result;
}
public new static KernelResult RemoveName(Horizon system, string name)
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
new file mode 100644
index 00000000..f139d3d4
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
@@ -0,0 +1,60 @@
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.HLE.HOS.Services;
+
+namespace Ryujinx.HLE.HOS.Kernel.Ipc
+{
+ class KClientSession : KSynchronizationObject
+ {
+ public KProcess CreatorProcess { get; }
+
+ private KSession _parent;
+
+ public ChannelState State { get; set; }
+
+ //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(Horizon system, KSession parent) : base(system)
+ {
+ _parent = parent;
+
+ State = ChannelState.Open;
+
+ CreatorProcess = system.Scheduler.GetCurrentProcess();
+
+ CreatorProcess.IncrementReferenceCount();
+ }
+
+ public KernelResult SendSyncRequest(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
+ {
+ KThread currentThread = System.Scheduler.GetCurrentThread();
+
+ KSessionRequest request = new KSessionRequest(currentThread, customCmdBuffAddr, customCmdBuffSize);
+
+ System.CriticalSection.Enter();
+
+ currentThread.SignaledObj = null;
+ currentThread.ObjSyncResult = KernelResult.Success;
+
+ KernelResult result = _parent.ServerSession.EnqueueRequest(request);
+
+ System.CriticalSection.Leave();
+
+ if (result == KernelResult.Success)
+ {
+ result = currentThread.ObjSyncResult;
+ }
+
+ return result;
+ }
+
+ protected override void Destroy()
+ {
+ _parent.DisconnectClient();
+ _parent.DecrementReferenceCount();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KLightClientSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KLightClientSession.cs
new file mode 100644
index 00000000..62c352bf
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KLightClientSession.cs
@@ -0,0 +1,14 @@
+using Ryujinx.HLE.HOS.Kernel.Common;
+
+namespace Ryujinx.HLE.HOS.Kernel.Ipc
+{
+ class KLightClientSession : KAutoObject
+ {
+ private KLightSession _parent;
+
+ public KLightClientSession(Horizon system, KLightSession parent) : base(system)
+ {
+ _parent = parent;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KLightServerSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KLightServerSession.cs
new file mode 100644
index 00000000..1ea2205d
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KLightServerSession.cs
@@ -0,0 +1,14 @@
+using Ryujinx.HLE.HOS.Kernel.Common;
+
+namespace Ryujinx.HLE.HOS.Kernel.Ipc
+{
+ class KLightServerSession : KAutoObject
+ {
+ private KLightSession _parent;
+
+ public KLightServerSession(Horizon system, KLightSession parent) : base(system)
+ {
+ _parent = parent;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs
new file mode 100644
index 00000000..a12a1986
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs
@@ -0,0 +1,20 @@
+using Ryujinx.HLE.HOS.Kernel.Common;
+
+namespace Ryujinx.HLE.HOS.Kernel.Ipc
+{
+ class KLightSession : KAutoObject
+ {
+ public KLightServerSession ServerSession { get; }
+ public KLightClientSession ClientSession { get; }
+
+ private bool _hasBeenInitialized;
+
+ public KLightSession(Horizon system) : base(system)
+ {
+ ServerSession = new KLightServerSession(system, this);
+ ClientSession = new KLightClientSession(system, this);
+
+ _hasBeenInitialized = true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KPort.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KPort.cs
index 16e9a111..9d93cf7b 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KPort.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KPort.cs
@@ -4,25 +4,68 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
class KPort : KAutoObject
{
- public KServerPort ServerPort { get; private set; }
- public KClientPort ClientPort { get; private set; }
+ public KServerPort ServerPort { get; }
+ public KClientPort ClientPort { get; }
private long _nameAddress;
- private bool _isLight;
- public KPort(Horizon system) : base(system)
+ private ChannelState _state;
+
+ public bool IsLight { get; private set; }
+
+ public KPort(Horizon system, int maxSessions, bool isLight, long nameAddress) : base(system)
{
- ServerPort = new KServerPort(system);
- ClientPort = new KClientPort(system);
+ ServerPort = new KServerPort(system, this);
+ ClientPort = new KClientPort(system, this, maxSessions);
+
+ IsLight = isLight;
+ _nameAddress = nameAddress;
+
+ _state = ChannelState.Open;
}
- public void Initialize(int maxSessions, bool isLight, long nameAddress)
+ public KernelResult EnqueueIncomingSession(KServerSession session)
{
- ServerPort.Initialize(this);
- ClientPort.Initialize(this, maxSessions);
+ KernelResult result;
- _isLight = isLight;
- _nameAddress = nameAddress;
+ System.CriticalSection.Enter();
+
+ if (_state == ChannelState.Open)
+ {
+ ServerPort.EnqueueIncomingSession(session);
+
+ result = KernelResult.Success;
+ }
+ else
+ {
+ result = KernelResult.PortClosed;
+ }
+
+ System.CriticalSection.Leave();
+
+ return result;
+ }
+
+ public KernelResult EnqueueIncomingLightSession(KLightServerSession session)
+ {
+ KernelResult result;
+
+ System.CriticalSection.Enter();
+
+ if (_state == ChannelState.Open)
+ {
+ ServerPort.EnqueueIncomingLightSession(session);
+
+ result = KernelResult.Success;
+ }
+ else
+ {
+ result = KernelResult.PortClosed;
+ }
+
+ System.CriticalSection.Leave();
+
+ return result;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerPort.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerPort.cs
index d4d3bcd2..919df357 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerPort.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerPort.cs
@@ -1,16 +1,87 @@
using Ryujinx.HLE.HOS.Kernel.Common;
+using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
class KServerPort : KSynchronizationObject
{
+ private LinkedList<KServerSession> _incomingConnections;
+ private LinkedList<KLightServerSession> _lightIncomingConnections;
+
private KPort _parent;
- public KServerPort(Horizon system) : base(system) { }
+ public bool IsLight => _parent.IsLight;
- public void Initialize(KPort parent)
+ public KServerPort(Horizon system, KPort parent) : base(system)
{
_parent = parent;
+
+ _incomingConnections = new LinkedList<KServerSession>();
+ _lightIncomingConnections = new LinkedList<KLightServerSession>();
+ }
+
+ public void EnqueueIncomingSession(KServerSession session)
+ {
+ AcceptIncomingConnection(_incomingConnections, session);
+ }
+
+ public void EnqueueIncomingLightSession(KLightServerSession session)
+ {
+ AcceptIncomingConnection(_lightIncomingConnections, session);
+ }
+
+ private void AcceptIncomingConnection<T>(LinkedList<T> list, T session)
+ {
+ System.CriticalSection.Enter();
+
+ list.AddLast(session);
+
+ if (list.Count == 1)
+ {
+ Signal();
+ }
+
+ System.CriticalSection.Leave();
+ }
+
+ public KServerSession AcceptIncomingConnection()
+ {
+ return AcceptIncomingConnection(_incomingConnections);
+ }
+
+ public KLightServerSession AcceptIncomingLightConnection()
+ {
+ return AcceptIncomingConnection(_lightIncomingConnections);
+ }
+
+ private T AcceptIncomingConnection<T>(LinkedList<T> list)
+ {
+ T session = default(T);
+
+ System.CriticalSection.Enter();
+
+ if (list.Count != 0)
+ {
+ session = list.First.Value;
+
+ list.RemoveFirst();
+ }
+
+ System.CriticalSection.Leave();
+
+ return session;
+ }
+
+ public override bool IsSignaled()
+ {
+ if (_parent.IsLight)
+ {
+ return _lightIncomingConnections.Count != 0;
+ }
+ else
+ {
+ return _incomingConnections.Count != 0;
+ }
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
new file mode 100644
index 00000000..5a45ff4a
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
@@ -0,0 +1,1262 @@
+using Ryujinx.Common;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Memory;
+using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Kernel.Ipc
+{
+ class KServerSession : KSynchronizationObject
+ {
+ private static readonly MemoryState[] IpcMemoryStates = new MemoryState[]
+ {
+ MemoryState.IpcBuffer3,
+ MemoryState.IpcBuffer0,
+ MemoryState.IpcBuffer1,
+ (MemoryState)0xfffce5d4 //This is invalid, shouldn't be accessed.
+ };
+
+ private struct Message
+ {
+ public ulong Address { get; }
+ public ulong DramAddress { get; }
+ public ulong Size { get; }
+ public bool IsCustom { get; }
+
+ public Message(KThread thread, ulong customCmdBuffAddress, ulong customCmdBuffSize)
+ {
+ IsCustom = customCmdBuffAddress != 0;
+
+ if (IsCustom)
+ {
+ Address = customCmdBuffAddress;
+ Size = customCmdBuffSize;
+
+ KProcess process = thread.Owner;
+
+ DramAddress = process.MemoryManager.GetDramAddressFromVa(Address);
+ }
+ else
+ {
+ Address = thread.TlsAddress;
+ DramAddress = thread.TlsDramAddress;
+ Size = 0x100;
+ }
+ }
+
+ public Message(KSessionRequest request) : this(
+ request.ClientThread,
+ request.CustomCmdBuffAddr,
+ request.CustomCmdBuffSize) { }
+ }
+
+ private struct MessageHeader
+ {
+ public uint Word0 { get; }
+ public uint Word1 { get; }
+ public uint Word2 { get; }
+
+ public uint PointerBuffersCount { get; }
+ public uint SendBuffersCount { get; }
+ public uint ReceiveBuffersCount { get; }
+ public uint ExchangeBuffersCount { get; }
+
+ public uint RawDataSizeInWords { get; }
+
+ public uint ReceiveListType { get; }
+
+ public uint MessageSizeInWords { get; }
+ public uint ReceiveListOffsetInWords { get; }
+ public uint ReceiveListOffset { get; }
+
+ public bool HasHandles { get; }
+
+ public bool HasPid { get; }
+
+ public uint CopyHandlesCount { get; }
+ public uint MoveHandlesCount { get; }
+
+ public MessageHeader(uint word0, uint word1, uint word2)
+ {
+ Word0 = word0;
+ Word1 = word1;
+ Word2 = word2;
+
+ HasHandles = word1 >> 31 != 0;
+
+ uint handleDescSizeInWords = 0;
+
+ if (HasHandles)
+ {
+ uint pidSize = (word2 & 1) * 8;
+
+ HasPid = pidSize != 0;
+
+ CopyHandlesCount = (word2 >> 1) & 0xf;
+ MoveHandlesCount = (word2 >> 5) & 0xf;
+
+ handleDescSizeInWords = (pidSize + CopyHandlesCount * 4 + MoveHandlesCount * 4) / 4;
+ }
+ else
+ {
+ HasPid = false;
+
+ CopyHandlesCount = 0;
+ MoveHandlesCount = 0;
+ }
+
+ PointerBuffersCount = (word0 >> 16) & 0xf;
+ SendBuffersCount = (word0 >> 20) & 0xf;
+ ReceiveBuffersCount = (word0 >> 24) & 0xf;
+ ExchangeBuffersCount = word0 >> 28;
+
+ uint pointerDescSizeInWords = PointerBuffersCount * 2;
+ uint sendDescSizeInWords = SendBuffersCount * 3;
+ uint receiveDescSizeInWords = ReceiveBuffersCount * 3;
+ uint exchangeDescSizeInWords = ExchangeBuffersCount * 3;
+
+ RawDataSizeInWords = word1 & 0x3ff;
+
+ ReceiveListType = (word1 >> 10) & 0xf;
+
+ ReceiveListOffsetInWords = (word1 >> 20) & 0x7ff;
+
+ uint paddingSizeInWords = HasHandles ? 3u : 2u;
+
+ MessageSizeInWords = pointerDescSizeInWords +
+ sendDescSizeInWords +
+ receiveDescSizeInWords +
+ exchangeDescSizeInWords +
+ RawDataSizeInWords +
+ paddingSizeInWords +
+ handleDescSizeInWords;
+
+ if (ReceiveListOffsetInWords == 0)
+ {
+ ReceiveListOffsetInWords = MessageSizeInWords;
+ }
+
+ ReceiveListOffset = ReceiveListOffsetInWords * 4;
+ }
+ }
+
+ private struct PointerBufferDesc
+ {
+ public uint ReceiveIndex { get; }
+
+ public uint BufferSize { get; }
+ public ulong BufferAddress { get; set; }
+
+ public PointerBufferDesc(ulong dword)
+ {
+ ReceiveIndex = (uint)dword & 0xf;
+ BufferSize = (uint)dword >> 16;
+
+ BufferAddress = (dword >> 2) & 0x70;
+ BufferAddress |= (dword >> 12) & 0xf;
+
+ BufferAddress = (BufferAddress << 32) | (dword >> 32);
+ }
+
+ public ulong Pack()
+ {
+ ulong dword = (ReceiveIndex & 0xf) | ((BufferSize & 0xffff) << 16);
+
+ dword |= BufferAddress << 32;
+ dword |= (BufferAddress >> 20) & 0xf000;
+ dword |= (BufferAddress >> 30) & 0xffc0;
+
+ return dword;
+ }
+ }
+
+ private KSession _parent;
+
+ private LinkedList<KSessionRequest> _requests;
+
+ private KSessionRequest _activeRequest;
+
+ public KServerSession(Horizon system, KSession parent) : base(system)
+ {
+ _parent = parent;
+
+ _requests = new LinkedList<KSessionRequest>();
+ }
+
+ public KernelResult EnqueueRequest(KSessionRequest request)
+ {
+ if (_parent.ClientSession.State != ChannelState.Open)
+ {
+ return KernelResult.PortRemoteClosed;
+ }
+
+ if (request.AsyncEvent == null)
+ {
+ if (request.ClientThread.ShallBeTerminated ||
+ request.ClientThread.SchedFlags == ThreadSchedState.TerminationPending)
+ {
+ return KernelResult.ThreadTerminating;
+ }
+
+ request.ClientThread.Reschedule(ThreadSchedState.Paused);
+ }
+
+ _requests.AddLast(request);
+
+ if (_requests.Count == 1)
+ {
+ Signal();
+ }
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult Receive(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
+ {
+ KThread serverThread = System.Scheduler.GetCurrentThread();
+ KProcess serverProcess = serverThread.Owner;
+
+ System.CriticalSection.Enter();
+
+ if (_parent.ClientSession.State != ChannelState.Open)
+ {
+ System.CriticalSection.Leave();
+
+ return KernelResult.PortRemoteClosed;
+ }
+
+ if (_activeRequest != null || !DequeueRequest(out KSessionRequest request))
+ {
+ System.CriticalSection.Leave();
+
+ return KernelResult.NotFound;
+ }
+
+ if (request.ClientThread == null)
+ {
+ System.CriticalSection.Leave();
+
+ return KernelResult.PortRemoteClosed;
+ }
+
+ KThread clientThread = request.ClientThread;
+ KProcess clientProcess = clientThread.Owner;
+
+ System.CriticalSection.Leave();
+
+ _activeRequest = request;
+
+ request.ServerProcess = serverProcess;
+
+ Message clientMsg = new Message(request);
+ Message serverMsg = new Message(serverThread, customCmdBuffAddr, customCmdBuffSize);
+
+ MessageHeader clientHeader = GetClientMessageHeader(clientMsg);
+ MessageHeader serverHeader = GetServerMessageHeader(serverMsg);
+
+ KernelResult serverResult = KernelResult.NotFound;
+ KernelResult clientResult = KernelResult.Success;
+
+ void CleanUpForError()
+ {
+ if (request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager) == KernelResult.Success)
+ {
+ request.BufferDescriptorTable.RestoreClientBuffers(clientProcess.MemoryManager);
+ }
+
+ CloseAllHandles(serverMsg, clientHeader, serverProcess);
+
+ System.CriticalSection.Enter();
+
+ _activeRequest = null;
+
+ if (_requests.Count != 0)
+ {
+ Signal();
+ }
+
+ System.CriticalSection.Leave();
+
+ WakeClientThread(request, clientResult);
+ }
+
+ if (clientHeader.ReceiveListType < 2 &&
+ clientHeader.ReceiveListOffset > clientMsg.Size)
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidCombination;
+ }
+ else if (clientHeader.ReceiveListType == 2 &&
+ clientHeader.ReceiveListOffset + 8 > clientMsg.Size)
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidCombination;
+ }
+ else if (clientHeader.ReceiveListType > 2 &&
+ clientHeader.ReceiveListType * 8 - 0x10 + clientHeader.ReceiveListOffset > clientMsg.Size)
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidCombination;
+ }
+
+ if (clientHeader.ReceiveListOffsetInWords < clientHeader.MessageSizeInWords)
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidCombination;
+ }
+
+ if (clientHeader.MessageSizeInWords * 4 > clientMsg.Size)
+ {
+ CleanUpForError();
+
+ return KernelResult.CmdBufferTooSmall;
+ }
+
+ ulong[] receiveList = GetReceiveList(
+ serverMsg,
+ serverHeader.ReceiveListType,
+ serverHeader.ReceiveListOffset);
+
+ serverProcess.CpuMemory.WriteUInt32((long)serverMsg.Address + 0, clientHeader.Word0);
+ serverProcess.CpuMemory.WriteUInt32((long)serverMsg.Address + 4, clientHeader.Word1);
+
+ uint offset;
+
+ //Copy handles.
+ if (clientHeader.HasHandles)
+ {
+ if (clientHeader.MoveHandlesCount != 0)
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidCombination;
+ }
+
+ serverProcess.CpuMemory.WriteUInt32((long)serverMsg.Address + 8, clientHeader.Word2);
+
+ offset = 3;
+
+ if (clientHeader.HasPid)
+ {
+ serverProcess.CpuMemory.WriteInt64((long)serverMsg.Address + offset * 4, clientProcess.Pid);
+
+ offset += 2;
+ }
+
+ for (int index = 0; index < clientHeader.CopyHandlesCount; index++)
+ {
+ int newHandle = 0;
+
+ int handle = System.Device.Memory.ReadInt32((long)clientMsg.DramAddress + offset * 4);
+
+ if (clientResult == KernelResult.Success && handle != 0)
+ {
+ clientResult = GetCopyObjectHandle(clientThread, serverProcess, handle, out newHandle);
+ }
+
+ serverProcess.CpuMemory.WriteInt32((long)serverMsg.Address + offset * 4, newHandle);
+
+ offset++;
+ }
+
+ for (int index = 0; index < clientHeader.MoveHandlesCount; index++)
+ {
+ int newHandle = 0;
+
+ int handle = System.Device.Memory.ReadInt32((long)clientMsg.DramAddress + offset * 4);
+
+ if (handle != 0)
+ {
+ if (clientResult == KernelResult.Success)
+ {
+ clientResult = GetMoveObjectHandle(clientProcess, serverProcess, handle, out newHandle);
+ }
+ else
+ {
+ clientProcess.HandleTable.CloseHandle(handle);
+ }
+ }
+
+ serverProcess.CpuMemory.WriteInt32((long)serverMsg.Address + offset * 4, newHandle);
+
+ offset++;
+ }
+
+ if (clientResult != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return serverResult;
+ }
+ }
+ else
+ {
+ offset = 2;
+ }
+
+ //Copy pointer/receive list buffers.
+ uint recvListDstOffset = 0;
+
+ for (int index = 0; index < clientHeader.PointerBuffersCount; index++)
+ {
+ ulong pointerDesc = System.Device.Memory.ReadUInt64((long)clientMsg.DramAddress + offset * 4);
+
+ PointerBufferDesc descriptor = new PointerBufferDesc(pointerDesc);
+
+ if (descriptor.BufferSize != 0)
+ {
+ clientResult = GetReceiveListAddress(
+ descriptor,
+ serverMsg,
+ serverHeader.ReceiveListType,
+ clientHeader.MessageSizeInWords,
+ receiveList,
+ ref recvListDstOffset,
+ out ulong recvListBufferAddress);
+
+ if (clientResult != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return serverResult;
+ }
+
+ clientResult = clientProcess.MemoryManager.CopyDataToCurrentProcess(
+ recvListBufferAddress,
+ descriptor.BufferSize,
+ descriptor.BufferAddress,
+ MemoryState.IsPoolAllocated,
+ MemoryState.IsPoolAllocated,
+ MemoryPermission.Read,
+ MemoryAttribute.Uncached,
+ MemoryAttribute.None);
+
+ if (clientResult != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return serverResult;
+ }
+
+ descriptor.BufferAddress = recvListBufferAddress;
+ }
+ else
+ {
+ descriptor.BufferAddress = 0;
+ }
+
+ serverProcess.CpuMemory.WriteUInt64((long)serverMsg.Address + offset * 4, descriptor.Pack());
+
+ offset += 2;
+ }
+
+ //Copy send, receive and exchange buffers.
+ uint totalBuffersCount =
+ clientHeader.SendBuffersCount +
+ clientHeader.ReceiveBuffersCount +
+ clientHeader.ExchangeBuffersCount;
+
+ for (int index = 0; index < totalBuffersCount; index++)
+ {
+ long clientDescAddress = (long)clientMsg.DramAddress + offset * 4;
+
+ uint descWord0 = System.Device.Memory.ReadUInt32(clientDescAddress + 0);
+ uint descWord1 = System.Device.Memory.ReadUInt32(clientDescAddress + 4);
+ uint descWord2 = System.Device.Memory.ReadUInt32(clientDescAddress + 8);
+
+ bool isSendDesc = index < clientHeader.SendBuffersCount;
+ bool isExchangeDesc = index >= clientHeader.SendBuffersCount + clientHeader.ReceiveBuffersCount;
+
+ bool notReceiveDesc = isSendDesc || isExchangeDesc;
+ bool isReceiveDesc = !notReceiveDesc;
+
+ MemoryPermission permission = index >= clientHeader.SendBuffersCount
+ ? MemoryPermission.ReadAndWrite
+ : MemoryPermission.Read;
+
+ uint sizeHigh4 = (descWord2 >> 24) & 0xf;
+
+ ulong bufferSize = descWord0 | (ulong)sizeHigh4 << 32;
+
+ ulong dstAddress = 0;
+
+ if (bufferSize != 0)
+ {
+ ulong bufferAddress;
+
+ bufferAddress = descWord2 >> 28;
+ bufferAddress |= ((descWord2 >> 2) & 7) << 4;
+
+ bufferAddress = (bufferAddress << 32) | descWord1;
+
+ MemoryState state = IpcMemoryStates[(descWord2 + 1) & 3];
+
+ clientResult = serverProcess.MemoryManager.MapBufferFromClientProcess(
+ bufferSize,
+ bufferAddress,
+ clientProcess.MemoryManager,
+ permission,
+ state,
+ notReceiveDesc,
+ out dstAddress);
+
+ if (clientResult != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return serverResult;
+ }
+
+ if (isSendDesc)
+ {
+ clientResult = request.BufferDescriptorTable.AddSendBuffer(bufferAddress, dstAddress, bufferSize, state);
+ }
+ else if (isReceiveDesc)
+ {
+ clientResult = request.BufferDescriptorTable.AddReceiveBuffer(bufferAddress, dstAddress, bufferSize, state);
+ }
+ else /* if (isExchangeDesc) */
+ {
+ clientResult = request.BufferDescriptorTable.AddExchangeBuffer(bufferAddress, dstAddress, bufferSize, state);
+ }
+
+ if (clientResult != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return serverResult;
+ }
+ }
+
+ descWord1 = (uint)dstAddress;
+
+ descWord2 &= 3;
+
+ descWord2 |= sizeHigh4 << 24;
+
+ descWord2 |= (uint)(dstAddress >> 34) & 0x3ffffffc;
+ descWord2 |= (uint)(dstAddress >> 4) & 0xf0000000;
+
+ long serverDescAddress = (long)serverMsg.Address + offset * 4;
+
+ serverProcess.CpuMemory.WriteUInt32(serverDescAddress + 0, descWord0);
+ serverProcess.CpuMemory.WriteUInt32(serverDescAddress + 4, descWord1);
+ serverProcess.CpuMemory.WriteUInt32(serverDescAddress + 8, descWord2);
+
+ offset += 3;
+ }
+
+ //Copy raw data.
+ if (clientHeader.RawDataSizeInWords != 0)
+ {
+ ulong copySrc = clientMsg.Address + offset * 4;
+ ulong copyDst = serverMsg.Address + offset * 4;
+
+ ulong copySize = clientHeader.RawDataSizeInWords * 4;
+
+ if (serverMsg.IsCustom || clientMsg.IsCustom)
+ {
+ MemoryPermission permission = clientMsg.IsCustom
+ ? MemoryPermission.None
+ : MemoryPermission.Read;
+
+ clientResult = clientProcess.MemoryManager.CopyDataToCurrentProcess(
+ copyDst,
+ copySize,
+ copySrc,
+ MemoryState.IsPoolAllocated,
+ MemoryState.IsPoolAllocated,
+ permission,
+ MemoryAttribute.Uncached,
+ MemoryAttribute.None);
+ }
+ else
+ {
+ copySrc = clientProcess.MemoryManager.GetDramAddressFromVa(copySrc);
+ copyDst = serverProcess.MemoryManager.GetDramAddressFromVa(copyDst);
+
+ System.Device.Memory.Copy(copyDst, copySrc, copySize);
+ }
+
+ if (clientResult != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return serverResult;
+ }
+ }
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult Reply(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
+ {
+ KThread serverThread = System.Scheduler.GetCurrentThread();
+ KProcess serverProcess = serverThread.Owner;
+
+ System.CriticalSection.Enter();
+
+ if (_activeRequest == null)
+ {
+ System.CriticalSection.Leave();
+
+ return KernelResult.InvalidState;
+ }
+
+ KSessionRequest request = _activeRequest;
+
+ _activeRequest = null;
+
+ if (_requests.Count != 0)
+ {
+ Signal();
+ }
+
+ System.CriticalSection.Leave();
+
+ KThread clientThread = request.ClientThread;
+ KProcess clientProcess = clientThread.Owner;
+
+ Message clientMsg = new Message(request);
+ Message serverMsg = new Message(serverThread, customCmdBuffAddr, customCmdBuffSize);
+
+ MessageHeader clientHeader = GetClientMessageHeader(clientMsg);
+ MessageHeader serverHeader = GetServerMessageHeader(serverMsg);
+
+ KernelResult clientResult = KernelResult.Success;
+ KernelResult serverResult = KernelResult.Success;
+
+ void CleanUpForError()
+ {
+ CloseAllHandles(clientMsg, serverHeader, clientProcess);
+
+ CancelRequest(request, clientResult);
+ }
+
+ if (clientHeader.ReceiveListType < 2 &&
+ clientHeader.ReceiveListOffset > clientMsg.Size)
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidCombination;
+ }
+ else if (clientHeader.ReceiveListType == 2 &&
+ clientHeader.ReceiveListOffset + 8 > clientMsg.Size)
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidCombination;
+ }
+ else if (clientHeader.ReceiveListType > 2 &&
+ clientHeader.ReceiveListType * 8 - 0x10 + clientHeader.ReceiveListOffset > clientMsg.Size)
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidCombination;
+ }
+
+ if (clientHeader.ReceiveListOffsetInWords < clientHeader.MessageSizeInWords)
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidCombination;
+ }
+
+ if (serverHeader.MessageSizeInWords * 4 > clientMsg.Size)
+ {
+ CleanUpForError();
+
+ return KernelResult.CmdBufferTooSmall;
+ }
+
+ if (serverHeader.SendBuffersCount != 0 ||
+ serverHeader.ReceiveBuffersCount != 0 ||
+ serverHeader.ExchangeBuffersCount != 0)
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidCombination;
+ }
+
+ //Read receive list.
+ ulong[] receiveList = GetReceiveList(
+ clientMsg,
+ clientHeader.ReceiveListType,
+ clientHeader.ReceiveListOffset);
+
+ //Copy receive and exchange buffers.
+ clientResult = request.BufferDescriptorTable.CopyBuffersToClient(clientProcess.MemoryManager);
+
+ if (clientResult != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return serverResult;
+ }
+
+ //Copy header.
+ System.Device.Memory.WriteUInt32((long)clientMsg.DramAddress + 0, serverHeader.Word0);
+ System.Device.Memory.WriteUInt32((long)clientMsg.DramAddress + 4, serverHeader.Word1);
+
+ //Copy handles.
+ uint offset;
+
+ if (serverHeader.HasHandles)
+ {
+ offset = 3;
+
+ System.Device.Memory.WriteUInt32((long)clientMsg.DramAddress + 8, serverHeader.Word2);
+
+ if (serverHeader.HasPid)
+ {
+ System.Device.Memory.WriteInt64((long)clientMsg.DramAddress + offset * 4, serverProcess.Pid);
+
+ offset += 2;
+ }
+
+ for (int index = 0; index < serverHeader.CopyHandlesCount; index++)
+ {
+ int newHandle = 0;
+
+ int handle = serverProcess.CpuMemory.ReadInt32((long)serverMsg.Address + offset * 4);
+
+ if (handle != 0)
+ {
+ GetCopyObjectHandle(serverThread, clientProcess, handle, out newHandle);
+ }
+
+ System.Device.Memory.WriteInt32((long)clientMsg.DramAddress + offset * 4, newHandle);
+
+ offset++;
+ }
+
+ for (int index = 0; index < serverHeader.MoveHandlesCount; index++)
+ {
+ int newHandle = 0;
+
+ int handle = serverProcess.CpuMemory.ReadInt32((long)serverMsg.Address + offset * 4);
+
+ if (handle != 0)
+ {
+ if (clientResult == KernelResult.Success)
+ {
+ clientResult = GetMoveObjectHandle(serverProcess, clientProcess, handle, out newHandle);
+ }
+ else
+ {
+ serverProcess.HandleTable.CloseHandle(handle);
+ }
+ }
+
+ System.Device.Memory.WriteInt32((long)clientMsg.DramAddress + offset * 4, newHandle);
+
+ offset++;
+ }
+ }
+ else
+ {
+ offset = 2;
+ }
+
+ //Copy pointer/receive list buffers.
+ uint recvListDstOffset = 0;
+
+ for (int index = 0; index < serverHeader.PointerBuffersCount; index++)
+ {
+ ulong pointerDesc = serverProcess.CpuMemory.ReadUInt64((long)serverMsg.Address + offset * 4);
+
+ PointerBufferDesc descriptor = new PointerBufferDesc(pointerDesc);
+
+ if (descriptor.BufferSize != 0)
+ {
+ clientResult = GetReceiveListAddress(
+ descriptor,
+ clientMsg,
+ clientHeader.ReceiveListType,
+ serverHeader.MessageSizeInWords,
+ receiveList,
+ ref recvListDstOffset,
+ out ulong recvListBufferAddress);
+
+ if (clientResult != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return serverResult;
+ }
+
+ clientResult = clientProcess.MemoryManager.CopyDataFromCurrentProcess(
+ recvListBufferAddress,
+ descriptor.BufferSize,
+ MemoryState.IsPoolAllocated,
+ MemoryState.IsPoolAllocated,
+ MemoryPermission.Read,
+ MemoryAttribute.Uncached,
+ MemoryAttribute.None,
+ descriptor.BufferAddress);
+
+ if (clientResult != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return serverResult;
+ }
+ }
+
+ offset += 2;
+ }
+
+ //Set send, receive and exchange buffer descriptors to zero.
+ uint totalBuffersCount =
+ serverHeader.SendBuffersCount +
+ serverHeader.ReceiveBuffersCount +
+ serverHeader.ExchangeBuffersCount;
+
+ for (int index = 0; index < totalBuffersCount; index++)
+ {
+ long dstDescAddress = (long)clientMsg.DramAddress + offset * 4;
+
+ System.Device.Memory.WriteUInt32(dstDescAddress + 0, 0);
+ System.Device.Memory.WriteUInt32(dstDescAddress + 4, 0);
+ System.Device.Memory.WriteUInt32(dstDescAddress + 8, 0);
+
+ offset += 3;
+ }
+
+ //Copy raw data.
+ if (serverHeader.RawDataSizeInWords != 0)
+ {
+ ulong copyDst = clientMsg.Address + offset * 4;
+ ulong copySrc = serverMsg.Address + offset * 4;
+
+ ulong copySize = serverHeader.RawDataSizeInWords * 4;
+
+ if (serverMsg.IsCustom || clientMsg.IsCustom)
+ {
+ MemoryPermission permission = clientMsg.IsCustom
+ ? MemoryPermission.None
+ : MemoryPermission.Read;
+
+ clientResult = clientProcess.MemoryManager.CopyDataFromCurrentProcess(
+ copyDst,
+ copySize,
+ MemoryState.IsPoolAllocated,
+ MemoryState.IsPoolAllocated,
+ permission,
+ MemoryAttribute.Uncached,
+ MemoryAttribute.None,
+ copySrc);
+ }
+ else
+ {
+ copyDst = clientProcess.MemoryManager.GetDramAddressFromVa(copyDst);
+ copySrc = serverProcess.MemoryManager.GetDramAddressFromVa(copySrc);
+
+ System.Device.Memory.Copy(copyDst, copySrc, copySize);
+ }
+ }
+
+ //Unmap buffers from server.
+ clientResult = request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager);
+
+ if (clientResult != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return serverResult;
+ }
+
+ WakeClientThread(request, clientResult);
+
+ return serverResult;
+ }
+
+ private MessageHeader GetClientMessageHeader(Message clientMsg)
+ {
+ uint word0 = System.Device.Memory.ReadUInt32((long)clientMsg.DramAddress + 0);
+ uint word1 = System.Device.Memory.ReadUInt32((long)clientMsg.DramAddress + 4);
+ uint word2 = System.Device.Memory.ReadUInt32((long)clientMsg.DramAddress + 8);
+
+ return new MessageHeader(word0, word1, word2);
+ }
+
+ private MessageHeader GetServerMessageHeader(Message serverMsg)
+ {
+ KProcess currentProcess = System.Scheduler.GetCurrentProcess();
+
+ uint word0 = currentProcess.CpuMemory.ReadUInt32((long)serverMsg.Address + 0);
+ uint word1 = currentProcess.CpuMemory.ReadUInt32((long)serverMsg.Address + 4);
+ uint word2 = currentProcess.CpuMemory.ReadUInt32((long)serverMsg.Address + 8);
+
+ return new MessageHeader(word0, word1, word2);
+ }
+
+ private KernelResult GetCopyObjectHandle(
+ KThread srcThread,
+ KProcess dstProcess,
+ int srcHandle,
+ out int dstHandle)
+ {
+ dstHandle = 0;
+
+ KProcess srcProcess = srcThread.Owner;
+
+ KAutoObject obj;
+
+ if (srcHandle == KHandleTable.SelfProcessHandle)
+ {
+ obj = srcProcess;
+ }
+ else if (srcHandle == KHandleTable.SelfThreadHandle)
+ {
+ obj = srcThread;
+ }
+ else
+ {
+ obj = srcProcess.HandleTable.GetObject<KAutoObject>(srcHandle);
+ }
+
+ if (obj != null)
+ {
+ return dstProcess.HandleTable.GenerateHandle(obj, out dstHandle);
+ }
+ else
+ {
+ return KernelResult.InvalidHandle;
+ }
+ }
+
+ private KernelResult GetMoveObjectHandle(
+ KProcess srcProcess,
+ KProcess dstProcess,
+ int srcHandle,
+ out int dstHandle)
+ {
+ dstHandle = 0;
+
+ KAutoObject obj = srcProcess.HandleTable.GetObject<KAutoObject>(srcHandle);
+
+ if (obj != null)
+ {
+ KernelResult result = dstProcess.HandleTable.GenerateHandle(obj, out dstHandle);
+
+ srcProcess.HandleTable.CloseHandle(srcHandle);
+
+ return result;
+ }
+ else
+ {
+ return KernelResult.InvalidHandle;
+ }
+ }
+
+ private ulong[] GetReceiveList(Message message, uint recvListType, uint recvListOffset)
+ {
+ int recvListSize = 0;
+
+ if (recvListType >= 3)
+ {
+ recvListSize = (int)recvListType - 2;
+ }
+ else if (recvListType == 2)
+ {
+ recvListSize = 1;
+ }
+
+ ulong[] receiveList = new ulong[recvListSize];
+
+ long recvListAddress = (long)message.DramAddress + recvListOffset;
+
+ for (int index = 0; index < recvListSize; index++)
+ {
+ receiveList[index] = System.Device.Memory.ReadUInt64(recvListAddress + index * 8);
+ }
+
+ return receiveList;
+ }
+
+ private KernelResult GetReceiveListAddress(
+ PointerBufferDesc descriptor,
+ Message message,
+ uint recvListType,
+ uint messageSizeInWords,
+ ulong[] receiveList,
+ ref uint dstOffset,
+ out ulong address)
+ {
+ ulong recvListBufferAddress = address = 0;
+
+ if (recvListType == 0)
+ {
+ return KernelResult.OutOfResource;
+ }
+ else if (recvListType == 1 || recvListType == 2)
+ {
+ ulong recvListBaseAddr;
+ ulong recvListEndAddr;
+
+ if (recvListType == 1)
+ {
+ recvListBaseAddr = message.Address + messageSizeInWords * 4;
+ recvListEndAddr = message.Address + message.Size;
+ }
+ else /* if (recvListType == 2) */
+ {
+ ulong packed = receiveList[0];
+
+ recvListBaseAddr = packed & 0x7fffffffff;
+
+ uint size = (uint)(packed >> 48);
+
+ if (size == 0)
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ recvListEndAddr = recvListBaseAddr + size;
+ }
+
+ recvListBufferAddress = BitUtils.AlignUp(recvListBaseAddr + dstOffset, 0x10);
+
+ ulong endAddress = recvListBufferAddress + descriptor.BufferSize;
+
+ dstOffset = (uint)endAddress - (uint)recvListBaseAddr;
+
+ if (recvListBufferAddress + descriptor.BufferSize <= recvListBufferAddress ||
+ recvListBufferAddress + descriptor.BufferSize > recvListEndAddr)
+ {
+ return KernelResult.OutOfResource;
+ }
+ }
+ else /* if (recvListType > 2) */
+ {
+ if (descriptor.ReceiveIndex >= receiveList.Length)
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong packed = receiveList[descriptor.ReceiveIndex];
+
+ recvListBufferAddress = packed & 0x7fffffffff;
+
+ uint size = (uint)(packed >> 48);
+
+ if (recvListBufferAddress == 0 || size == 0 || size < descriptor.BufferSize)
+ {
+ return KernelResult.OutOfResource;
+ }
+ }
+
+ address = recvListBufferAddress;
+
+ return KernelResult.Success;
+ }
+
+ private void CloseAllHandles(Message message, MessageHeader header, KProcess process)
+ {
+ if (header.HasHandles)
+ {
+ uint totalHandeslCount = header.CopyHandlesCount + header.MoveHandlesCount;
+
+ uint offset = 3;
+
+ if (header.HasPid)
+ {
+ process.CpuMemory.WriteInt64((long)message.Address + offset * 4, 0);
+
+ offset += 2;
+ }
+
+ for (int index = 0; index < totalHandeslCount; index++)
+ {
+ int handle = process.CpuMemory.ReadInt32((long)message.Address + offset * 4);
+
+ if (handle != 0)
+ {
+ process.HandleTable.CloseHandle(handle);
+
+ process.CpuMemory.WriteInt32((long)message.Address + offset * 4, 0);
+ }
+
+ offset++;
+ }
+ }
+ }
+
+ public override bool IsSignaled()
+ {
+ if (_parent.ClientSession.State != ChannelState.Open)
+ {
+ return true;
+ }
+
+ return _requests.Count != 0 && _activeRequest == null;
+ }
+
+ protected override void Destroy()
+ {
+ _parent.DisconnectServer();
+
+ CancelAllRequestsServerDisconnected();
+
+ _parent.DecrementReferenceCount();
+ }
+
+ private void CancelAllRequestsServerDisconnected()
+ {
+ foreach (KSessionRequest request in IterateWithRemovalOfAllRequests())
+ {
+ CancelRequest(request, KernelResult.PortRemoteClosed);
+ }
+ }
+
+ public void CancelAllRequestsClientDisconnected()
+ {
+ foreach (KSessionRequest request in IterateWithRemovalOfAllRequests())
+ {
+ if (request.ClientThread.ShallBeTerminated ||
+ request.ClientThread.SchedFlags == ThreadSchedState.TerminationPending)
+ {
+ continue;
+ }
+
+ //Client sessions can only be disconnected on async requests (because
+ //the client would be otherwise blocked waiting for the response), so
+ //we only need to handle the async case here.
+ if (request.AsyncEvent != null)
+ {
+ SendResultToAsyncRequestClient(request, KernelResult.PortRemoteClosed);
+ }
+ }
+
+ WakeServerThreads(KernelResult.PortRemoteClosed);
+ }
+
+ private IEnumerable<KSessionRequest> IterateWithRemovalOfAllRequests()
+ {
+ System.CriticalSection.Enter();
+
+ if (_activeRequest != null)
+ {
+ KSessionRequest request = _activeRequest;
+
+ _activeRequest = null;
+
+ System.CriticalSection.Leave();
+
+ yield return request;
+ }
+ else
+ {
+ System.CriticalSection.Leave();
+ }
+
+ while (DequeueRequest(out KSessionRequest request))
+ {
+ yield return request;
+ }
+ }
+
+ private bool DequeueRequest(out KSessionRequest request)
+ {
+ request = null;
+
+ System.CriticalSection.Enter();
+
+ bool hasRequest = _requests.First != null;
+
+ if (hasRequest)
+ {
+ request = _requests.First.Value;
+
+ _requests.RemoveFirst();
+ }
+
+ System.CriticalSection.Leave();
+
+ return hasRequest;
+ }
+
+ private void CancelRequest(KSessionRequest request, KernelResult result)
+ {
+ KProcess clientProcess = request.ClientThread.Owner;
+ KProcess serverProcess = request.ServerProcess;
+
+ KernelResult unmapResult = KernelResult.Success;
+
+ if (serverProcess != null)
+ {
+ unmapResult = request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager);
+ }
+
+ if (unmapResult == KernelResult.Success)
+ {
+ request.BufferDescriptorTable.RestoreClientBuffers(clientProcess.MemoryManager);
+ }
+
+ WakeClientThread(request, result);
+ }
+
+ private void WakeClientThread(KSessionRequest request, KernelResult result)
+ {
+ //Wait client thread waiting for a response for the given request.
+ if (request.AsyncEvent != null)
+ {
+ SendResultToAsyncRequestClient(request, result);
+ }
+ else
+ {
+ System.CriticalSection.Enter();
+
+ WakeAndSetResult(request.ClientThread, result);
+
+ System.CriticalSection.Leave();
+ }
+ }
+
+ private void SendResultToAsyncRequestClient(KSessionRequest request, KernelResult result)
+ {
+ KProcess clientProcess = request.ClientThread.Owner;
+
+ ulong address = clientProcess.MemoryManager.GetDramAddressFromVa(request.CustomCmdBuffAddr);
+
+ System.Device.Memory.WriteInt64((long)address + 0, 0);
+ System.Device.Memory.WriteInt32((long)address + 8, (int)result);
+
+ clientProcess.MemoryManager.UnborrowIpcBuffer(
+ request.CustomCmdBuffAddr,
+ request.CustomCmdBuffSize);
+
+ request.AsyncEvent.Signal();
+ }
+
+ private void WakeServerThreads(KernelResult result)
+ {
+ //Wake all server threads waiting for requests.
+ System.CriticalSection.Enter();
+
+ foreach (KThread thread in WaitingThreads)
+ {
+ WakeAndSetResult(thread, result);
+ }
+
+ System.CriticalSection.Leave();
+ }
+
+ private void WakeAndSetResult(KThread thread, KernelResult result)
+ {
+ if ((thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
+ {
+ thread.SignaledObj = null;
+ thread.ObjSyncResult = result;
+
+ thread.Reschedule(ThreadSchedState.Running);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs
index f2b30493..cbf689a5 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs
@@ -1,18 +1,40 @@
-using Ryujinx.HLE.HOS.Services;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Process;
using System;
namespace Ryujinx.HLE.HOS.Kernel.Ipc
{
- class KSession : IDisposable
+ class KSession : KAutoObject, IDisposable
{
- public IpcService Service { get; private set; }
+ public KServerSession ServerSession { get; }
+ public KClientSession ClientSession { get; }
- public string ServiceName { get; private set; }
+ private bool _hasBeenInitialized;
- public KSession(IpcService service, string serviceName)
+ public KSession(Horizon system) : base(system)
{
- Service = service;
- ServiceName = serviceName;
+ ServerSession = new KServerSession(system, this);
+ ClientSession = new KClientSession(system, this);
+
+ _hasBeenInitialized = true;
+ }
+
+ public void DisconnectClient()
+ {
+ if (ClientSession.State == ChannelState.Open)
+ {
+ ClientSession.State = ChannelState.ClientDisconnected;
+
+ ServerSession.CancelAllRequestsClientDisconnected();
+ }
+ }
+
+ public void DisconnectServer()
+ {
+ if (ClientSession.State == ChannelState.Open)
+ {
+ ClientSession.State = ChannelState.ServerDisconnected;
+ }
}
public void Dispose()
@@ -22,10 +44,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
protected virtual void Dispose(bool disposing)
{
- if (disposing && Service is IDisposable disposableService)
+ if (disposing && ClientSession.Service is IDisposable disposableService)
{
disposableService.Dispose();
}
}
+
+ protected override void Destroy()
+ {
+ if (_hasBeenInitialized)
+ {
+ KProcess creatorProcess = ClientSession.CreatorProcess;
+
+ creatorProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
+
+ creatorProcess.DecrementReferenceCount();
+ }
+ }
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs
new file mode 100644
index 00000000..f3467f39
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs
@@ -0,0 +1,31 @@
+using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel.Ipc
+{
+ class KSessionRequest
+ {
+ public KBufferDescriptorTable BufferDescriptorTable { get; }
+
+ public KThread ClientThread { get; }
+
+ public KProcess ServerProcess { get; set; }
+
+ public KWritableEvent AsyncEvent { get; }
+
+ public ulong CustomCmdBuffAddr { get; }
+ public ulong CustomCmdBuffSize { get; }
+
+ public KSessionRequest(
+ KThread clientThread,
+ ulong customCmdBuffAddr,
+ ulong customCmdBuffSize)
+ {
+ ClientThread = clientThread;
+ CustomCmdBuffAddr = customCmdBuffAddr;
+ CustomCmdBuffSize = customCmdBuffSize;
+
+ BufferDescriptorTable = new KBufferDescriptorTable();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs
index 89a19498..b7c2b309 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs
@@ -1,29 +1,108 @@
+using System;
+
namespace Ryujinx.HLE.HOS.Kernel.Memory
{
class KMemoryBlock
{
- public ulong BaseAddress { get; set; }
- public ulong PagesCount { get; set; }
+ public ulong BaseAddress { get; private set; }
+ public ulong PagesCount { get; private set; }
- public MemoryState State { get; set; }
- public MemoryPermission Permission { get; set; }
- public MemoryAttribute Attribute { get; set; }
+ public MemoryState State { get; private set; }
+ public MemoryPermission Permission { get; private set; }
+ public MemoryAttribute Attribute { get; private set; }
+ public MemoryPermission SourcePermission { get; private set; }
- public int IpcRefCount { get; set; }
- public int DeviceRefCount { get; set; }
+ public int IpcRefCount { get; private set; }
+ public int DeviceRefCount { get; private set; }
public KMemoryBlock(
ulong baseAddress,
ulong pagesCount,
MemoryState state,
MemoryPermission permission,
- MemoryAttribute attribute)
+ MemoryAttribute attribute,
+ int ipcRefCount = 0,
+ int deviceRefCount = 0)
+ {
+ BaseAddress = baseAddress;
+ PagesCount = pagesCount;
+ State = state;
+ Attribute = attribute;
+ Permission = permission;
+ IpcRefCount = ipcRefCount;
+ DeviceRefCount = deviceRefCount;
+ }
+
+ public void SetState(MemoryPermission permission, MemoryState state, MemoryAttribute attribute)
+ {
+ Permission = permission;
+ State = state;
+ Attribute &= MemoryAttribute.IpcAndDeviceMapped;
+ Attribute |= attribute;
+ }
+
+ public void SetIpcMappingPermission(MemoryPermission permission)
+ {
+ int oldIpcRefCount = IpcRefCount++;
+
+ if ((ushort)IpcRefCount == 0)
+ {
+ throw new InvalidOperationException("IPC reference count increment overflowed.");
+ }
+
+ if (oldIpcRefCount == 0)
+ {
+ SourcePermission = permission;
+
+ Permission &= ~MemoryPermission.ReadAndWrite;
+ Permission |= MemoryPermission.ReadAndWrite & permission;
+ }
+
+ Attribute |= MemoryAttribute.IpcMapped;
+ }
+
+ public void RestoreIpcMappingPermission()
+ {
+ int oldIpcRefCount = IpcRefCount--;
+
+ if (oldIpcRefCount == 0)
+ {
+ throw new InvalidOperationException("IPC reference count decrement underflowed.");
+ }
+
+ if (oldIpcRefCount == 1)
+ {
+ Permission = SourcePermission;
+
+ SourcePermission = MemoryPermission.None;
+
+ Attribute &= ~MemoryAttribute.IpcMapped;
+ }
+ }
+
+ public KMemoryBlock SplitRightAtAddress(ulong address)
+ {
+ ulong leftAddress = BaseAddress;
+
+ ulong leftPagesCount = (address - leftAddress) / KMemoryManager.PageSize;
+
+ BaseAddress = address;
+
+ PagesCount -= leftPagesCount;
+
+ return new KMemoryBlock(
+ leftAddress,
+ leftPagesCount,
+ State,
+ Permission,
+ Attribute,
+ IpcRefCount,
+ DeviceRefCount);
+ }
+
+ public void AddPages(ulong pagesCount)
{
- BaseAddress = baseAddress;
- PagesCount = pagesCount;
- State = state;
- Attribute = attribute;
- Permission = permission;
+ PagesCount += pagesCount;
}
public KMemoryInfo GetInfo()
@@ -36,6 +115,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
State,
Permission,
Attribute,
+ SourcePermission,
IpcRefCount,
DeviceRefCount);
}
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs
index 226ce77c..21e9e494 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs
@@ -2,15 +2,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
class KMemoryInfo
{
- public ulong Address { get; private set; }
- public ulong Size { get; private set; }
+ public ulong Address { get; }
+ public ulong Size { get; }
- public MemoryState State { get; private set; }
- public MemoryPermission Permission { get; private set; }
- public MemoryAttribute Attribute { get; private set; }
+ public MemoryState State { get; }
+ public MemoryPermission Permission { get; }
+ public MemoryAttribute Attribute { get; }
+ public MemoryPermission SourcePermission { get; }
- public int IpcRefCount { get; private set; }
- public int DeviceRefCount { get; private set; }
+ public int IpcRefCount { get; }
+ public int DeviceRefCount { get; }
public KMemoryInfo(
ulong address,
@@ -18,16 +19,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
MemoryState state,
MemoryPermission permission,
MemoryAttribute attribute,
+ MemoryPermission sourcePermission,
int ipcRefCount,
int deviceRefCount)
{
- Address = address;
- Size = size;
- State = state;
- Attribute = attribute;
- Permission = permission;
- IpcRefCount = ipcRefCount;
- DeviceRefCount = deviceRefCount;
+ Address = address;
+ Size = size;
+ State = state;
+ Permission = permission;
+ Attribute = attribute;
+ SourcePermission = sourcePermission;
+ IpcRefCount = ipcRefCount;
+ DeviceRefCount = deviceRefCount;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
index fb5dec04..7a40139c 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
@@ -9,6 +9,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
class KMemoryManager
{
+ private static readonly int[] MappingUnitSizes = new int[]
+ {
+ 0x1000,
+ 0x10000,
+ 0x200000,
+ 0x400000,
+ 0x2000000,
+ 0x40000000
+ };
+
public const int PageSize = 0x1000;
private const int KMemoryBlockSize = 0x40;
@@ -335,7 +345,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong addrSpacePagesCount = (addrSpaceEnd - addrSpaceStart) / PageSize;
- InsertBlock(addrSpaceStart, addrSpacePagesCount, MemoryState.Unmapped);
+ _blocks.AddFirst(new KMemoryBlock(
+ addrSpaceStart,
+ addrSpacePagesCount,
+ MemoryState.Unmapped,
+ MemoryPermission.None,
+ MemoryAttribute.None));
return KernelResult.Success;
}
@@ -488,67 +503,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
return KernelResult.OutOfMemory;
}
- ulong reservedPagesCount = _isKernel ? 1UL : 4UL;
-
lock (_blocks)
{
- if (_aslrEnabled)
- {
- ulong totalNeededSize = (reservedPagesCount + neededPagesCount) * PageSize;
-
- ulong remainingPages = regionPagesCount - neededPagesCount;
-
- ulong aslrMaxOffset = ((remainingPages + reservedPagesCount) * PageSize) / (ulong)alignment;
-
- for (int attempt = 0; attempt < 8; attempt++)
- {
- address = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, alignment);
-
- ulong endAddr = address + totalNeededSize;
-
- KMemoryInfo info = FindBlock(address).GetInfo();
-
- if (info.State != MemoryState.Unmapped)
- {
- continue;
- }
-
- ulong currBaseAddr = info.Address + reservedPagesCount * PageSize;
- ulong currEndAddr = info.Address + info.Size;
-
- if (address >= regionStart &&
- address >= currBaseAddr &&
- endAddr - 1 <= regionEndAddr - 1 &&
- endAddr - 1 <= currEndAddr - 1)
- {
- break;
- }
- }
-
- if (address == 0)
- {
- ulong aslrPage = GetRandomValue(0, aslrMaxOffset);
-
- address = FindFirstFit(
- regionStart + aslrPage * PageSize,
- regionPagesCount - aslrPage,
- neededPagesCount,
- alignment,
- 0,
- reservedPagesCount);
- }
- }
-
- if (address == 0)
- {
- address = FindFirstFit(
- regionStart,
- regionPagesCount,
- neededPagesCount,
- alignment,
- 0,
- reservedPagesCount);
- }
+ address = AllocateVa(regionStart, regionPagesCount, neededPagesCount, alignment);
if (address == 0)
{
@@ -977,6 +934,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
MemoryState.Reserved,
MemoryPermission.None,
MemoryAttribute.None,
+ MemoryPermission.None,
0,
0);
}
@@ -1325,22 +1283,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
ulong mappedSize = 0;
- KMemoryInfo info;
-
- LinkedListNode<KMemoryBlock> node = FindBlockNode(address);
-
- do
+ foreach (KMemoryInfo info in IterateOverRange(address, endAddr))
{
- info = node.Value.GetInfo();
-
if (info.State != MemoryState.Unmapped)
{
mappedSize += GetSizeInRange(info, address, endAddr);
}
-
- node = node.Next;
}
- while (info.Address + info.Size < endAddr && node != null);
if (mappedSize == size)
{
@@ -1419,16 +1368,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
KPageList pageList = new KPageList();
- KMemoryInfo info;
-
- LinkedListNode<KMemoryBlock> baseNode = FindBlockNode(address);
-
- LinkedListNode<KMemoryBlock> node = baseNode;
-
- do
+ foreach (KMemoryInfo info in IterateOverRange(address, endAddr))
{
- info = node.Value.GetInfo();
-
if (info.State == MemoryState.Heap)
{
if (info.Attribute != MemoryAttribute.None)
@@ -1447,10 +1388,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
return KernelResult.InvalidMemState;
}
-
- node = node.Next;
}
- while (info.Address + info.Size < endAddr && node != null);
if (heapMappedSize == 0)
{
@@ -1465,12 +1403,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
//Try to unmap all the heap mapped memory inside range.
KernelResult result = KernelResult.Success;
- node = baseNode;
-
- do
+ foreach (KMemoryInfo info in IterateOverRange(address, endAddr))
{
- info = node.Value.GetInfo();
-
if (info.State == MemoryState.Heap)
{
ulong blockSize = GetSizeInRange(info, address, endAddr);
@@ -1488,10 +1422,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
break;
}
}
-
- node = node.Next;
}
- while (info.Address + info.Size < endAddr && node != null);
if (result == KernelResult.Success)
{
@@ -1514,10 +1445,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
private void MapPhysicalMemory(KPageList pageList, ulong address, ulong endAddr)
{
- KMemoryInfo info;
-
- LinkedListNode<KMemoryBlock> node = FindBlockNode(address);
-
LinkedListNode<KPageNode> pageListNode = pageList.Nodes.First;
KPageNode pageNode = pageListNode.Value;
@@ -1525,10 +1452,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong srcPa = pageNode.Address;
ulong srcPaPages = pageNode.PagesCount;
- do
+ foreach (KMemoryInfo info in IterateOverRange(address, endAddr))
{
- info = node.Value.GetInfo();
-
if (info.State == MemoryState.Unmapped)
{
ulong blockSize = GetSizeInRange(info, address, endAddr);
@@ -1570,38 +1495,750 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
dstVaPages -= pagesCount;
}
}
+ }
+ }
- node = node.Next;
+ public KernelResult CopyDataToCurrentProcess(
+ ulong dst,
+ ulong size,
+ ulong src,
+ MemoryState stateMask,
+ MemoryState stateExpected,
+ MemoryPermission permission,
+ MemoryAttribute attributeMask,
+ MemoryAttribute attributeExpected)
+ {
+ //Client -> server.
+ return CopyDataFromOrToCurrentProcess(
+ size,
+ src,
+ dst,
+ stateMask,
+ stateExpected,
+ permission,
+ attributeMask,
+ attributeExpected,
+ toServer: true);
+ }
+
+ public KernelResult CopyDataFromCurrentProcess(
+ ulong dst,
+ ulong size,
+ MemoryState stateMask,
+ MemoryState stateExpected,
+ MemoryPermission permission,
+ MemoryAttribute attributeMask,
+ MemoryAttribute attributeExpected,
+ ulong src)
+ {
+ //Server -> client.
+ return CopyDataFromOrToCurrentProcess(
+ size,
+ dst,
+ src,
+ stateMask,
+ stateExpected,
+ permission,
+ attributeMask,
+ attributeExpected,
+ toServer: false);
+ }
+
+ private KernelResult CopyDataFromOrToCurrentProcess(
+ ulong size,
+ ulong clientAddress,
+ ulong serverAddress,
+ MemoryState stateMask,
+ MemoryState stateExpected,
+ MemoryPermission permission,
+ MemoryAttribute attributeMask,
+ MemoryAttribute attributeExpected,
+ bool toServer)
+ {
+ if (AddrSpaceStart > clientAddress)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ ulong srcEndAddr = clientAddress + size;
+
+ if (srcEndAddr <= clientAddress || srcEndAddr - 1 > AddrSpaceEnd - 1)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ lock (_blocks)
+ {
+ if (CheckRange(
+ clientAddress,
+ size,
+ stateMask,
+ stateExpected,
+ permission,
+ permission,
+ attributeMask | MemoryAttribute.Uncached,
+ attributeExpected))
+ {
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ serverAddress = currentProcess.MemoryManager.GetDramAddressFromVa(serverAddress);
+
+ if (toServer)
+ {
+ _system.Device.Memory.Copy(serverAddress, GetDramAddressFromVa(clientAddress), size);
+ }
+ else
+ {
+ _system.Device.Memory.Copy(GetDramAddressFromVa(clientAddress), serverAddress, size);
+ }
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
+ }
}
- while (info.Address + info.Size < endAddr && node != null);
}
- private static ulong GetSizeInRange(KMemoryInfo info, ulong start, ulong end)
+ public KernelResult MapBufferFromClientProcess(
+ ulong size,
+ ulong src,
+ KMemoryManager sourceMemMgr,
+ MemoryPermission permission,
+ MemoryState state,
+ bool copyData,
+ out ulong dst)
{
- ulong endAddr = info.Size + info.Address;
- ulong size = info.Size;
+ dst = 0;
- if (info.Address < start)
+ KernelResult result = sourceMemMgr.GetPagesForMappingIntoAnotherProcess(
+ src,
+ size,
+ permission,
+ state,
+ copyData,
+ _aslrDisabled,
+ _memRegion,
+ out KPageList pageList);
+
+ if (result != KernelResult.Success)
{
- size -= start - info.Address;
+ return result;
}
- if (endAddr > end)
+ result = MapPagesFromAnotherProcess(size, src, permission, state, pageList, out ulong va);
+
+ if (result != KernelResult.Success)
{
- size -= endAddr - end;
+ sourceMemMgr.UnmapIpcRestorePermission(src, size, state);
+ }
+ else
+ {
+ dst = va;
}
- return size;
+ return result;
}
- private static ulong GetAddrInRange(KMemoryInfo info, ulong start)
+ private KernelResult GetPagesForMappingIntoAnotherProcess(
+ ulong address,
+ ulong size,
+ MemoryPermission permission,
+ MemoryState state,
+ bool copyData,
+ bool aslrDisabled,
+ MemoryRegion region,
+ out KPageList pageList)
{
- if (info.Address < start)
+ pageList = null;
+
+ if (AddrSpaceStart > address)
{
- return start;
+ return KernelResult.InvalidMemState;
}
- return info.Address;
+ ulong endAddr = address + size;
+
+ if (endAddr <= address || endAddr - 1 > AddrSpaceEnd - 1)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ MemoryState stateMask;
+
+ switch (state)
+ {
+ case MemoryState.IpcBuffer0: stateMask = MemoryState.IpcSendAllowedType0; break;
+ case MemoryState.IpcBuffer1: stateMask = MemoryState.IpcSendAllowedType1; break;
+ case MemoryState.IpcBuffer3: stateMask = MemoryState.IpcSendAllowedType3; break;
+
+ default: return KernelResult.InvalidCombination;
+ }
+
+ MemoryPermission permissionMask = permission == MemoryPermission.ReadAndWrite
+ ? MemoryPermission.None
+ : MemoryPermission.Read;
+
+ MemoryAttribute attributeMask = MemoryAttribute.Borrowed | MemoryAttribute.Uncached;
+
+ if (state == MemoryState.IpcBuffer0)
+ {
+ attributeMask |= MemoryAttribute.DeviceMapped;
+ }
+
+ ulong addressRounded = BitUtils.AlignUp (address, PageSize);
+ ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
+ ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
+
+ if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong visitedSize = 0;
+
+ void CleanUpForError()
+ {
+ ulong endAddrVisited = address + visitedSize;
+
+ foreach (KMemoryInfo info in IterateOverRange(address, endAddrVisited))
+ {
+ if ((info.Permission & MemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
+ {
+ ulong blockAddress = GetAddrInRange(info, addressRounded);
+ ulong blockSize = GetSizeInRange(info, addressRounded, endAddrVisited);
+
+ ulong blockPagesCount = blockSize / PageSize;
+
+ if (DoMmuOperation(
+ blockAddress,
+ blockPagesCount,
+ 0,
+ false,
+ info.Permission,
+ MemoryOperation.ChangePermRw) != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Unexpected failure trying to restore permission.");
+ }
+ }
+ }
+ }
+
+ lock (_blocks)
+ {
+ KernelResult result;
+
+ foreach (KMemoryInfo info in IterateOverRange(address, endAddrRounded))
+ {
+ //Check if the block state matches what we expect.
+ if ((info.State & stateMask) != stateMask ||
+ (info.Permission & permission) != permission ||
+ (info.Attribute & attributeMask) != MemoryAttribute.None)
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidMemState;
+ }
+
+ 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);
+
+ if (result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return result;
+ }
+
+ if (visitedSize != 0)
+ {
+ InsertBlock(address, visitedSize / PageSize, SetIpcMappingPermissions, permissionMask);
+ }
+ }
+
+ return KernelResult.Success;
+ }
+
+ private KernelResult GetPagesForIpcTransfer(
+ ulong address,
+ ulong size,
+ bool copyData,
+ bool aslrDisabled,
+ MemoryRegion region,
+ out KPageList pageList)
+ {
+ pageList = null;
+
+ 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;
+
+ void CleanUpForError()
+ {
+ FreeSinglePage(region, dstFirstPagePa);
+
+ if (dstLastPagePa != 0)
+ {
+ FreeSinglePage(region, dstLastPagePa);
+ }
+ }
+
+ ulong firstPageFillAddress = dstFirstPagePa;
+
+ if (!ConvertVaToPa(addressTruncated, out ulong srcFirstPagePa))
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidMemState;
+ }
+
+ ulong unusedSizeAfter;
+
+ //When the start address is unaligned, we can't safely map the
+ //first page as it would expose other undesirable information on the
+ //target process. So, instead we allocate new pages, copy the data
+ //inside the range, and then clear the remaining space.
+ //The same also holds for the last page, if the end address
+ //(address + size) is also not aligned.
+ if (copyData)
+ {
+ ulong unusedSizeBefore = address - addressTruncated;
+
+ _system.Device.Memory.Set(dstFirstPagePa, 0, unusedSizeBefore);
+
+ ulong copySize = addressRounded <= endAddr ? addressRounded - address : size;
+
+ _system.Device.Memory.Copy(
+ GetDramAddressFromPa(dstFirstPagePa + unusedSizeBefore),
+ GetDramAddressFromPa(srcFirstPagePa + unusedSizeBefore), copySize);
+
+ firstPageFillAddress += unusedSizeBefore + copySize;
+
+ unusedSizeAfter = addressRounded > endAddr ? addressRounded - endAddr : 0;
+ }
+ else
+ {
+ unusedSizeAfter = PageSize;
+ }
+
+ if (unusedSizeAfter != 0)
+ {
+ _system.Device.Memory.Set(firstPageFillAddress, 0, unusedSizeAfter);
+ }
+
+ KPageList pages = new KPageList();
+
+ if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return KernelResult.OutOfResource;
+ }
+
+ ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
+ ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
+
+ if (endAddrTruncated > addressRounded)
+ {
+ ulong alignedPagesCount = (endAddrTruncated - addressRounded) / PageSize;
+
+ AddVaRangeToPageList(pages, addressRounded, alignedPagesCount);
+ }
+
+ if (endAddrTruncated != endAddrRounded)
+ {
+ //End is also not aligned...
+ dstLastPagePa = AllocateSinglePage(region, aslrDisabled);
+
+ if (dstLastPagePa == 0)
+ {
+ CleanUpForError();
+
+ return KernelResult.OutOfMemory;
+ }
+
+ ulong lastPageFillAddr = dstLastPagePa;
+
+ if (!ConvertVaToPa(endAddrTruncated, out ulong srcLastPagePa))
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidMemState;
+ }
+
+ if (copyData)
+ {
+ ulong copySize = endAddr - endAddrTruncated;
+
+ _system.Device.Memory.Copy(
+ GetDramAddressFromPa(dstLastPagePa),
+ GetDramAddressFromPa(srcLastPagePa), copySize);
+
+ lastPageFillAddr += copySize;
+
+ unusedSizeAfter = PageSize - copySize;
+ }
+ else
+ {
+ unusedSizeAfter = PageSize;
+ }
+
+ _system.Device.Memory.Set(lastPageFillAddr, 0, unusedSizeAfter);
+
+ if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return KernelResult.OutOfResource;
+ }
+ }
+
+ pageList = pages;
+
+ return KernelResult.Success;
+ }
+
+ private ulong AllocateSinglePage(MemoryRegion region, bool aslrDisabled)
+ {
+ KMemoryRegionManager regionMgr = _system.MemoryRegions[(int)region];
+
+ return regionMgr.AllocatePagesContiguous(1, aslrDisabled);
+ }
+
+ private void FreeSinglePage(MemoryRegion region, ulong address)
+ {
+ KMemoryRegionManager regionMgr = _system.MemoryRegions[(int)region];
+
+ regionMgr.FreePage(address);
+ }
+
+ private KernelResult MapPagesFromAnotherProcess(
+ ulong size,
+ ulong address,
+ MemoryPermission permission,
+ MemoryState state,
+ KPageList pageList,
+ out ulong mappedVa)
+ {
+ mappedVa = 0;
+
+ lock (_blocks)
+ {
+ if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong endAddr = address + size;
+
+ ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
+ ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
+
+ ulong neededSize = endAddrRounded - addressTruncated;
+
+ ulong neededPagesCount = neededSize / PageSize;
+
+ ulong regionPagesCount = (AliasRegionEnd - AliasRegionStart) / PageSize;
+
+ ulong va = 0;
+
+ for (int unit = MappingUnitSizes.Length - 1; unit >= 0 && va == 0; unit--)
+ {
+ int alignemnt = MappingUnitSizes[unit];
+
+ va = AllocateVa(AliasRegionStart, regionPagesCount, neededPagesCount, alignemnt);
+ }
+
+ if (va == 0)
+ {
+ return KernelResult.OutOfVaSpace;
+ }
+
+ if (pageList.Nodes.Count != 0)
+ {
+ KernelResult result = MapPages(va, pageList, permission);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+ }
+
+ InsertBlock(va, neededPagesCount, state, permission);
+
+ mappedVa = va;
+ }
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult UnmapNoAttributeIfStateEquals(ulong address, ulong size, MemoryState state)
+ {
+ if (AddrSpaceStart > address)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ ulong endAddr = address + size;
+
+ if (endAddr <= address || endAddr - 1 > AddrSpaceEnd - 1)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ lock (_blocks)
+ {
+ if (CheckRange(
+ address,
+ size,
+ MemoryState.Mask,
+ state,
+ MemoryPermission.Read,
+ MemoryPermission.Read,
+ MemoryAttribute.Mask,
+ MemoryAttribute.None,
+ MemoryAttribute.IpcAndDeviceMapped,
+ out _,
+ out _,
+ out _))
+ {
+ if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
+ ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
+
+ ulong pagesCount = (endAddrRounded - addressTruncated) / PageSize;
+
+ KernelResult result = DoMmuOperation(
+ addressTruncated,
+ pagesCount,
+ 0,
+ false,
+ MemoryPermission.None,
+ MemoryOperation.Unmap);
+
+ if (result == KernelResult.Success)
+ {
+ InsertBlock(addressTruncated, pagesCount, MemoryState.Unmapped);
+ }
+
+ return result;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
+ }
+ }
+ }
+
+ public KernelResult UnmapIpcRestorePermission(ulong address, ulong size, MemoryState state)
+ {
+ ulong endAddr = address + size;
+
+ ulong addressRounded = BitUtils.AlignUp (address, PageSize);
+ ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
+
+ ulong pagesCount = (endAddrTruncated - addressRounded) / PageSize;
+
+ MemoryState stateMask;
+
+ switch (state)
+ {
+ case MemoryState.IpcBuffer0: stateMask = MemoryState.IpcSendAllowedType0; break;
+ case MemoryState.IpcBuffer1: stateMask = MemoryState.IpcSendAllowedType1; break;
+ case MemoryState.IpcBuffer3: stateMask = MemoryState.IpcSendAllowedType3; break;
+
+ default: return KernelResult.InvalidCombination;
+ }
+
+ MemoryAttribute attributeMask =
+ MemoryAttribute.Borrowed |
+ MemoryAttribute.IpcMapped |
+ MemoryAttribute.Uncached;
+
+ if (state == MemoryState.IpcBuffer0)
+ {
+ attributeMask |= MemoryAttribute.DeviceMapped;
+ }
+
+ if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ lock (_blocks)
+ {
+ foreach (KMemoryInfo info in IterateOverRange(address, endAddrTruncated))
+ {
+ //Check if the block state matches what we expect.
+ if ((info.State & stateMask) != stateMask ||
+ (info.Attribute & attributeMask) != MemoryAttribute.IpcMapped)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (info.Permission != info.SourcePermission && info.IpcRefCount == 1)
+ {
+ ulong blockAddress = GetAddrInRange(info, addressRounded);
+ ulong blockSize = GetSizeInRange(info, addressRounded, endAddrTruncated);
+
+ ulong blockPagesCount = blockSize / PageSize;
+
+ KernelResult result = DoMmuOperation(
+ blockAddress,
+ blockPagesCount,
+ 0,
+ false,
+ info.SourcePermission,
+ MemoryOperation.ChangePermRw);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+ }
+ }
+ }
+
+ InsertBlock(address, pagesCount, RestoreIpcMappingPermissions);
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult UnborrowIpcBuffer(ulong address, ulong size)
+ {
+ return ClearAttributesAndChangePermission(
+ address,
+ size,
+ MemoryState.IpcBufferAllowed,
+ MemoryState.IpcBufferAllowed,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.Borrowed,
+ MemoryPermission.ReadAndWrite,
+ MemoryAttribute.Borrowed);
+ }
+
+ private KernelResult ClearAttributesAndChangePermission(
+ ulong address,
+ ulong size,
+ MemoryState stateMask,
+ MemoryState stateExpected,
+ MemoryPermission permissionMask,
+ MemoryPermission permissionExpected,
+ MemoryAttribute attributeMask,
+ MemoryAttribute attributeExpected,
+ MemoryPermission newPermission,
+ MemoryAttribute attributeClearMask,
+ KPageList pageList = null)
+ {
+ 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 & ~attributeClearMask;
+
+ InsertBlock(address, pagesCount, oldState, newPermission, newAttribute);
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
+ }
+ }
}
private void AddVaRangeToPageList(KPageList pageList, ulong start, ulong pagesCount)
@@ -1610,9 +2247,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
while (address < start + pagesCount * PageSize)
{
- KernelResult result = ConvertVaToPa(address, out ulong pa);
-
- if (result != KernelResult.Success)
+ if (!ConvertVaToPa(address, out ulong pa))
{
throw new InvalidOperationException("Unexpected failure translating virtual address.");
}
@@ -1623,6 +2258,34 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
}
}
+ private static ulong GetAddrInRange(KMemoryInfo info, ulong start)
+ {
+ if (info.Address < start)
+ {
+ return start;
+ }
+
+ return info.Address;
+ }
+
+ private static ulong GetSizeInRange(KMemoryInfo info, ulong start, ulong end)
+ {
+ ulong endAddr = info.Size + info.Address;
+ ulong size = info.Size;
+
+ if (info.Address < start)
+ {
+ size -= start - info.Address;
+ }
+
+ if (endAddr > end)
+ {
+ size -= endAddr - end;
+ }
+
+ return size;
+ }
+
private bool IsUnmapped(ulong address, ulong size)
{
return CheckRange(
@@ -1654,7 +2317,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
out MemoryPermission outPermission,
out MemoryAttribute outAttribute)
{
- ulong endAddr = address + size - 1;
+ ulong endAddr = address + size;
LinkedListNode<KMemoryBlock> node = FindBlockNode(address);
@@ -1676,28 +2339,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
(firstState & stateMask) != stateExpected ||
(firstPermission & permissionMask) != permissionExpected)
{
- break;
- }
-
- //Check if this is the last block on the range, if so return success.
- if (endAddr <= info.Address + info.Size - 1)
- {
- outState = firstState;
- outPermission = firstPermission;
- outAttribute = firstAttribute & ~attributeIgnoreMask;
+ outState = MemoryState.Unmapped;
+ outPermission = MemoryPermission.None;
+ outAttribute = MemoryAttribute.None;
- return true;
+ return false;
}
-
- node = node.Next;
}
- while (node != null);
+ while (info.Address + info.Size - 1 < endAddr - 1 && (node = node.Next) != null);
- outState = MemoryState.Unmapped;
- outPermission = MemoryPermission.None;
- outAttribute = MemoryAttribute.None;
+ outState = firstState;
+ outPermission = firstPermission;
+ outAttribute = firstAttribute & ~attributeIgnoreMask;
- return false;
+ return true;
}
private bool CheckRange(
@@ -1710,33 +2365,33 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
MemoryAttribute attributeMask,
MemoryAttribute attributeExpected)
{
- ulong endAddr = address + size - 1;
-
- LinkedListNode<KMemoryBlock> node = FindBlockNode(address);
-
- do
+ foreach (KMemoryInfo info in IterateOverRange(address, address + size))
{
- KMemoryInfo info = node.Value.GetInfo();
-
//Check if the block state matches what we expect.
if ((info.State & stateMask) != stateExpected ||
(info.Permission & permissionMask) != permissionExpected ||
(info.Attribute & attributeMask) != attributeExpected)
{
- break;
+ return false;
}
+ }
- //Check if this is the last block on the range, if so return success.
- if (endAddr <= info.Address + info.Size - 1)
- {
- return true;
- }
+ return true;
+ }
- node = node.Next;
- }
- while (node != null);
+ private IEnumerable<KMemoryInfo> IterateOverRange(ulong start, ulong end)
+ {
+ LinkedListNode<KMemoryBlock> node = FindBlockNode(start);
+
+ KMemoryInfo info;
+
+ do
+ {
+ info = node.Value.GetInfo();
- return false;
+ yield return info;
+ }
+ while (info.Address + info.Size - 1 < end - 1 && (node = node.Next) != null);
}
private void InsertBlock(
@@ -1750,23 +2405,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
MemoryAttribute newAttribute)
{
//Insert new block on the list only on areas where the state
- //of the block matches the state specified on the Old* state
+ //of the block matches the state specified on the old* state
//arguments, otherwise leave it as is.
int oldCount = _blocks.Count;
oldAttribute |= MemoryAttribute.IpcAndDeviceMapped;
- ulong endAddr = pagesCount * PageSize + baseAddress;
+ ulong endAddr = baseAddress + pagesCount * PageSize;
LinkedListNode<KMemoryBlock> node = _blocks.First;
while (node != null)
{
- LinkedListNode<KMemoryBlock> newNode = node;
- LinkedListNode<KMemoryBlock> nextNode = node.Next;
-
KMemoryBlock currBlock = node.Value;
+ LinkedListNode<KMemoryBlock> nextNode = node.Next;
+
ulong currBaseAddr = currBlock.BaseAddress;
ulong currEndAddr = currBlock.PagesCount * PageSize + currBaseAddr;
@@ -1783,67 +2437,28 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
continue;
}
- if (currBaseAddr >= baseAddress && currEndAddr <= endAddr)
+ LinkedListNode<KMemoryBlock> newNode = node;
+
+ if (baseAddress > currBaseAddr)
{
- currBlock.State = newState;
- currBlock.Permission = newPermission;
- currBlock.Attribute &= ~MemoryAttribute.IpcAndDeviceMapped;
- currBlock.Attribute |= newAttribute;
+ _blocks.AddBefore(node, currBlock.SplitRightAtAddress(baseAddress));
}
- else if (currBaseAddr >= baseAddress)
- {
- currBlock.BaseAddress = endAddr;
- currBlock.PagesCount = (currEndAddr - endAddr) / PageSize;
-
- ulong newPagesCount = (endAddr - currBaseAddr) / PageSize;
-
- newNode = _blocks.AddBefore(node, new KMemoryBlock(
- currBaseAddr,
- newPagesCount,
- newState,
- newPermission,
- newAttribute));
- }
- else if (currEndAddr <= endAddr)
+ if (endAddr < currEndAddr)
{
- currBlock.PagesCount = (baseAddress - currBaseAddr) / PageSize;
-
- ulong newPagesCount = (currEndAddr - baseAddress) / PageSize;
-
- newNode = _blocks.AddAfter(node, new KMemoryBlock(
- baseAddress,
- newPagesCount,
- newState,
- newPermission,
- newAttribute));
+ newNode = _blocks.AddBefore(node, currBlock.SplitRightAtAddress(endAddr));
}
- else
- {
- currBlock.PagesCount = (baseAddress - currBaseAddr) / PageSize;
-
- ulong nextPagesCount = (currEndAddr - endAddr) / PageSize;
-
- newNode = _blocks.AddAfter(node, new KMemoryBlock(
- baseAddress,
- pagesCount,
- newState,
- newPermission,
- newAttribute));
-
- _blocks.AddAfter(newNode, new KMemoryBlock(
- endAddr,
- nextPagesCount,
- currBlock.State,
- currBlock.Permission,
- currBlock.Attribute));
- nextNode = null;
- }
+ newNode.Value.SetState(newPermission, newState, newAttribute);
MergeEqualStateNeighbours(newNode);
}
+ if (currEndAddr - 1 >= endAddr - 1)
+ {
+ break;
+ }
+
node = nextNode;
}
@@ -1859,13 +2474,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
//Inserts new block at the list, replacing and spliting
//existing blocks as needed.
- KMemoryBlock block = new KMemoryBlock(baseAddress, pagesCount, state, permission, attribute);
-
int oldCount = _blocks.Count;
- ulong endAddr = pagesCount * PageSize + baseAddress;
-
- LinkedListNode<KMemoryBlock> newNode = null;
+ ulong endAddr = baseAddress + pagesCount * PageSize;
LinkedListNode<KMemoryBlock> node = _blocks.First;
@@ -1880,69 +2491,99 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
if (baseAddress < currEndAddr && currBaseAddr < endAddr)
{
- if (baseAddress >= currBaseAddr && endAddr <= currEndAddr)
+ LinkedListNode<KMemoryBlock> newNode = node;
+
+ if (baseAddress > currBaseAddr)
{
- block.Attribute |= currBlock.Attribute & MemoryAttribute.IpcAndDeviceMapped;
+ _blocks.AddBefore(node, currBlock.SplitRightAtAddress(baseAddress));
}
- if (baseAddress > currBaseAddr && endAddr < currEndAddr)
+ if (endAddr < currEndAddr)
{
- currBlock.PagesCount = (baseAddress - currBaseAddr) / PageSize;
+ newNode = _blocks.AddBefore(node, currBlock.SplitRightAtAddress(endAddr));
+ }
- ulong nextPagesCount = (currEndAddr - endAddr) / PageSize;
+ newNode.Value.SetState(permission, state, attribute);
- newNode = _blocks.AddAfter(node, block);
+ MergeEqualStateNeighbours(newNode);
+ }
- _blocks.AddAfter(newNode, new KMemoryBlock(
- endAddr,
- nextPagesCount,
- currBlock.State,
- currBlock.Permission,
- currBlock.Attribute));
+ if (currEndAddr - 1 >= endAddr - 1)
+ {
+ break;
+ }
- break;
- }
- else if (baseAddress <= currBaseAddr && endAddr < currEndAddr)
- {
- currBlock.BaseAddress = endAddr;
+ node = nextNode;
+ }
- currBlock.PagesCount = (currEndAddr - endAddr) / PageSize;
+ _blockAllocator.Count += _blocks.Count - oldCount;
+ }
- if (newNode == null)
- {
- newNode = _blocks.AddBefore(node, block);
- }
- }
- else if (baseAddress > currBaseAddr && endAddr >= currEndAddr)
- {
- currBlock.PagesCount = (baseAddress - currBaseAddr) / PageSize;
+ private static void SetIpcMappingPermissions(KMemoryBlock block, MemoryPermission permission)
+ {
+ block.SetIpcMappingPermission(permission);
+ }
- if (newNode == null)
- {
- newNode = _blocks.AddAfter(node, block);
- }
- }
- else
+ private static void RestoreIpcMappingPermissions(KMemoryBlock block, MemoryPermission permission)
+ {
+ block.RestoreIpcMappingPermission();
+ }
+
+ private delegate void BlockMutator(KMemoryBlock block, MemoryPermission newPerm);
+
+ private void InsertBlock(
+ ulong baseAddress,
+ ulong pagesCount,
+ BlockMutator blockMutate,
+ MemoryPermission permission = MemoryPermission.None)
+ {
+ //Inserts new block at the list, replacing and spliting
+ //existing blocks as needed, then calling the callback
+ //function on the new block.
+ int oldCount = _blocks.Count;
+
+ ulong endAddr = baseAddress + pagesCount * PageSize;
+
+ LinkedListNode<KMemoryBlock> node = _blocks.First;
+
+ while (node != null)
+ {
+ KMemoryBlock currBlock = node.Value;
+
+ LinkedListNode<KMemoryBlock> nextNode = node.Next;
+
+ ulong currBaseAddr = currBlock.BaseAddress;
+ ulong currEndAddr = currBlock.PagesCount * PageSize + currBaseAddr;
+
+ if (baseAddress < currEndAddr && currBaseAddr < endAddr)
+ {
+ LinkedListNode<KMemoryBlock> newNode = node;
+
+ if (baseAddress > currBaseAddr)
{
- if (newNode == null)
- {
- newNode = _blocks.AddBefore(node, block);
- }
+ _blocks.AddBefore(node, currBlock.SplitRightAtAddress(baseAddress));
+ }
- _blocks.Remove(node);
+ if (endAddr < currEndAddr)
+ {
+ newNode = _blocks.AddBefore(node, currBlock.SplitRightAtAddress(endAddr));
}
+
+ KMemoryBlock newBlock = newNode.Value;
+
+ blockMutate(newBlock, permission);
+
+ MergeEqualStateNeighbours(newNode);
}
- node = nextNode;
- }
+ if (currEndAddr - 1 >= endAddr - 1)
+ {
+ break;
+ }
- if (newNode == null)
- {
- newNode = _blocks.AddFirst(block);
+ node = nextNode;
}
- MergeEqualStateNeighbours(newNode);
-
_blockAllocator.Count += _blocks.Count - oldCount;
}
@@ -1950,42 +2591,117 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
KMemoryBlock block = node.Value;
- ulong endAddr = block.PagesCount * PageSize + block.BaseAddress;
-
if (node.Previous != null)
{
- KMemoryBlock previous = node.Previous.Value;
+ KMemoryBlock previousBlock = node.Previous.Value;
- if (BlockStateEquals(block, previous))
+ if (BlockStateEquals(block, previousBlock))
{
- _blocks.Remove(node.Previous);
+ LinkedListNode<KMemoryBlock> previousNode = node.Previous;
- block.BaseAddress = previous.BaseAddress;
+ _blocks.Remove(node);
+
+ previousBlock.AddPages(block.PagesCount);
+
+ node = previousNode;
+ block = previousBlock;
}
}
if (node.Next != null)
{
- KMemoryBlock next = node.Next.Value;
+ KMemoryBlock nextBlock = node.Next.Value;
- if (BlockStateEquals(block, next))
+ if (BlockStateEquals(block, nextBlock))
{
_blocks.Remove(node.Next);
- endAddr = next.BaseAddress + next.PagesCount * PageSize;
+ block.AddPages(nextBlock.PagesCount);
}
}
-
- block.PagesCount = (endAddr - block.BaseAddress) / PageSize;
}
private static bool BlockStateEquals(KMemoryBlock lhs, KMemoryBlock rhs)
{
- return lhs.State == rhs.State &&
- lhs.Permission == rhs.Permission &&
- lhs.Attribute == rhs.Attribute &&
- lhs.DeviceRefCount == rhs.DeviceRefCount &&
- lhs.IpcRefCount == rhs.IpcRefCount;
+ return lhs.State == rhs.State &&
+ lhs.Permission == rhs.Permission &&
+ lhs.Attribute == rhs.Attribute &&
+ lhs.SourcePermission == rhs.SourcePermission &&
+ lhs.DeviceRefCount == rhs.DeviceRefCount &&
+ lhs.IpcRefCount == rhs.IpcRefCount;
+ }
+
+ private ulong AllocateVa(
+ ulong regionStart,
+ ulong regionPagesCount,
+ ulong neededPagesCount,
+ int alignment)
+ {
+ ulong address = 0;
+
+ ulong regionEndAddr = regionStart + regionPagesCount * PageSize;
+
+ ulong reservedPagesCount = _isKernel ? 1UL : 4UL;
+
+ if (_aslrEnabled)
+ {
+ ulong totalNeededSize = (reservedPagesCount + neededPagesCount) * PageSize;
+
+ ulong remainingPages = regionPagesCount - neededPagesCount;
+
+ ulong aslrMaxOffset = ((remainingPages + reservedPagesCount) * PageSize) / (ulong)alignment;
+
+ for (int attempt = 0; attempt < 8; attempt++)
+ {
+ address = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, alignment);
+
+ ulong endAddr = address + totalNeededSize;
+
+ KMemoryInfo info = FindBlock(address).GetInfo();
+
+ if (info.State != MemoryState.Unmapped)
+ {
+ continue;
+ }
+
+ ulong currBaseAddr = info.Address + reservedPagesCount * PageSize;
+ ulong currEndAddr = info.Address + info.Size;
+
+ if (address >= regionStart &&
+ address >= currBaseAddr &&
+ endAddr - 1 <= regionEndAddr - 1 &&
+ endAddr - 1 <= currEndAddr - 1)
+ {
+ break;
+ }
+ }
+
+ if (address == 0)
+ {
+ ulong aslrPage = GetRandomValue(0, aslrMaxOffset);
+
+ address = FindFirstFit(
+ regionStart + aslrPage * PageSize,
+ regionPagesCount - aslrPage,
+ neededPagesCount,
+ alignment,
+ 0,
+ reservedPagesCount);
+ }
+ }
+
+ if (address == 0)
+ {
+ address = FindFirstFit(
+ regionStart,
+ regionPagesCount,
+ neededPagesCount,
+ alignment,
+ 0,
+ reservedPagesCount);
+ }
+
+ return address;
}
private ulong FindFirstFit(
@@ -2397,11 +3113,21 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
return KernelResult.Success;
}
- public KernelResult ConvertVaToPa(ulong va, out ulong pa)
+ public ulong GetDramAddressFromVa(ulong va)
+ {
+ return (ulong)_cpuMemory.GetPhysicalAddress((long)va);
+ }
+
+ public bool ConvertVaToPa(ulong va, out ulong pa)
{
pa = DramMemoryMap.DramBase + (ulong)_cpuMemory.GetPhysicalAddress((long)va);
- return KernelResult.Success;
+ return true;
+ }
+
+ public static ulong GetDramAddressFromPa(ulong pa)
+ {
+ return pa - DramMemoryMap.DramBase;
}
public long GetMmUsedPages()
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs
index 777e9aa9..92cef559 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs
@@ -94,6 +94,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
}
}
+ public ulong AllocatePagesContiguous(ulong pagesCount, bool backwards)
+ {
+ lock (_blocks)
+ {
+ return AllocatePagesContiguousImpl(pagesCount, backwards);
+ }
+ }
+
private KernelResult AllocatePagesImpl(ulong pagesCount, bool backwards, out KPageList pageList)
{
pageList = new KPageList();
@@ -122,165 +130,216 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
//If so, try allocating as much requested pages as possible.
while (blockPagesCount <= pagesCount)
{
- ulong address = 0;
+ ulong address = AllocatePagesForOrder(blockIndex, backwards, bestFitBlockSize);
- for (int currBlockIndex = blockIndex;
- currBlockIndex < _blockOrdersCount && address == 0;
- currBlockIndex++)
+ //The address being zero means that no free space was found on that order,
+ //just give up and try with the next one.
+ if (address == 0)
{
- block = _blocks[currBlockIndex];
+ break;
+ }
- int index = 0;
+ //Add new allocated page(s) to the pages list.
+ //If an error occurs, then free all allocated pages and fail.
+ KernelResult result = pageList.AddRange(address, blockPagesCount);
- bool zeroMask = false;
+ if (result != KernelResult.Success)
+ {
+ FreePages(address, blockPagesCount);
- for (int level = 0; level < block.MaxLevel; level++)
+ foreach (KPageNode pageNode in pageList)
{
- long mask = block.Masks[level][index];
-
- if (mask == 0)
- {
- zeroMask = true;
-
- break;
- }
-
- if (backwards)
- {
- index = (index * 64 + 63) - BitUtils.CountLeadingZeros64(mask);
- }
- else
- {
- index = index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(mask));
- }
+ FreePages(pageNode.Address, pageNode.PagesCount);
}
- if (block.SizeInBlocksTruncated <= (ulong)index || zeroMask)
- {
- continue;
- }
+ return result;
+ }
- block.FreeCount--;
+ pagesCount -= blockPagesCount;
+ }
+ }
- int tempIdx = index;
+ //Success case, all requested pages were allocated successfully.
+ if (pagesCount == 0)
+ {
+ return KernelResult.Success;
+ }
- for (int level = block.MaxLevel - 1; level >= 0; level--, tempIdx /= 64)
- {
- block.Masks[level][tempIdx / 64] &= ~(1L << (tempIdx & 63));
+ //Error case, free allocated pages and return out of memory.
+ foreach (KPageNode pageNode in pageList)
+ {
+ FreePages(pageNode.Address, pageNode.PagesCount);
+ }
- if (block.Masks[level][tempIdx / 64] != 0)
- {
- break;
- }
- }
+ pageList = null;
- address = block.StartAligned + ((ulong)index << block.Order);
- }
+ return KernelResult.OutOfMemory;
+ }
- for (int currBlockIndex = blockIndex;
- currBlockIndex < _blockOrdersCount && address == 0;
- currBlockIndex++)
- {
- block = _blocks[currBlockIndex];
+ private ulong AllocatePagesContiguousImpl(ulong pagesCount, bool backwards)
+ {
+ if (pagesCount == 0 || _blocks.Length < 1)
+ {
+ return 0;
+ }
- int index = 0;
+ int blockIndex = 0;
- bool zeroMask = false;
+ while ((1UL << _blocks[blockIndex].Order) / KMemoryManager.PageSize < pagesCount)
+ {
+ if (++blockIndex >= _blocks.Length)
+ {
+ return 0;
+ }
+ }
- for (int level = 0; level < block.MaxLevel; level++)
- {
- long mask = block.Masks[level][index];
-
- if (mask == 0)
- {
- zeroMask = true;
-
- break;
- }
-
- if (backwards)
- {
- index = index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(mask));
- }
- else
- {
- index = (index * 64 + 63) - BitUtils.CountLeadingZeros64(mask);
- }
- }
+ ulong tightestFitBlockSize = 1UL << _blocks[blockIndex].Order;
- if (block.SizeInBlocksTruncated <= (ulong)index || zeroMask)
- {
- continue;
- }
+ ulong address = AllocatePagesForOrder(blockIndex, backwards, tightestFitBlockSize);
- block.FreeCount--;
+ ulong requiredSize = pagesCount * KMemoryManager.PageSize;
- int tempIdx = index;
+ if (address != 0 && tightestFitBlockSize > requiredSize)
+ {
+ FreePages(address + requiredSize, (tightestFitBlockSize - requiredSize) / KMemoryManager.PageSize);
+ }
- for (int level = block.MaxLevel - 1; level >= 0; level--, tempIdx /= 64)
- {
- block.Masks[level][tempIdx / 64] &= ~(1L << (tempIdx & 63));
+ return address;
+ }
- if (block.Masks[level][tempIdx / 64] != 0)
- {
- break;
- }
- }
+ private ulong AllocatePagesForOrder(int blockIndex, bool backwards, ulong bestFitBlockSize)
+ {
+ ulong address = 0;
- address = block.StartAligned + ((ulong)index << block.Order);
- }
+ KMemoryRegionBlock block = null;
- //The address being zero means that no free space was found on that order,
- //just give up and try with the next one.
- if (address == 0)
+ for (int currBlockIndex = blockIndex;
+ currBlockIndex < _blockOrdersCount && address == 0;
+ currBlockIndex++)
+ {
+ block = _blocks[currBlockIndex];
+
+ int index = 0;
+
+ bool zeroMask = false;
+
+ for (int level = 0; level < block.MaxLevel; level++)
+ {
+ long mask = block.Masks[level][index];
+
+ if (mask == 0)
{
+ zeroMask = true;
+
break;
}
- //If we are using a larger order than best fit, then we should
- //split it into smaller blocks.
- ulong firstFreeBlockSize = 1UL << block.Order;
+ if (backwards)
+ {
+ index = (index * 64 + 63) - BitUtils.CountLeadingZeros64(mask);
+ }
+ else
+ {
+ index = index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(mask));
+ }
+ }
- if (firstFreeBlockSize > bestFitBlockSize)
+ if (block.SizeInBlocksTruncated <= (ulong)index || zeroMask)
+ {
+ continue;
+ }
+
+ block.FreeCount--;
+
+ int tempIdx = index;
+
+ for (int level = block.MaxLevel - 1; level >= 0; level--, tempIdx /= 64)
+ {
+ block.Masks[level][tempIdx / 64] &= ~(1L << (tempIdx & 63));
+
+ if (block.Masks[level][tempIdx / 64] != 0)
{
- FreePages(address + bestFitBlockSize, (firstFreeBlockSize - bestFitBlockSize) / KMemoryManager.PageSize);
+ break;
}
+ }
- //Add new allocated page(s) to the pages list.
- //If an error occurs, then free all allocated pages and fail.
- KernelResult result = pageList.AddRange(address, blockPagesCount);
+ address = block.StartAligned + ((ulong)index << block.Order);
+ }
- if (result != KernelResult.Success)
+ for (int currBlockIndex = blockIndex;
+ currBlockIndex < _blockOrdersCount && address == 0;
+ currBlockIndex++)
+ {
+ block = _blocks[currBlockIndex];
+
+ int index = 0;
+
+ bool zeroMask = false;
+
+ for (int level = 0; level < block.MaxLevel; level++)
+ {
+ long mask = block.Masks[level][index];
+
+ if (mask == 0)
{
- FreePages(address, blockPagesCount);
+ zeroMask = true;
- foreach (KPageNode pageNode in pageList)
- {
- FreePages(pageNode.Address, pageNode.PagesCount);
- }
+ break;
+ }
- return result;
+ if (backwards)
+ {
+ index = index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(mask));
}
+ else
+ {
+ index = (index * 64 + 63) - BitUtils.CountLeadingZeros64(mask);
+ }
+ }
- pagesCount -= blockPagesCount;
+ if (block.SizeInBlocksTruncated <= (ulong)index || zeroMask)
+ {
+ continue;
}
- }
- //Success case, all requested pages were allocated successfully.
- if (pagesCount == 0)
- {
- return KernelResult.Success;
+ block.FreeCount--;
+
+ int tempIdx = index;
+
+ for (int level = block.MaxLevel - 1; level >= 0; level--, tempIdx /= 64)
+ {
+ block.Masks[level][tempIdx / 64] &= ~(1L << (tempIdx & 63));
+
+ if (block.Masks[level][tempIdx / 64] != 0)
+ {
+ break;
+ }
+ }
+
+ address = block.StartAligned + ((ulong)index << block.Order);
}
- //Error case, free allocated pages and return out of memory.
- foreach (KPageNode pageNode in pageList)
+ if (address != 0)
{
- FreePages(pageNode.Address, pageNode.PagesCount);
+ //If we are using a larger order than best fit, then we should
+ //split it into smaller blocks.
+ ulong firstFreeBlockSize = 1UL << block.Order;
+
+ if (firstFreeBlockSize > bestFitBlockSize)
+ {
+ FreePages(address + bestFitBlockSize, (firstFreeBlockSize - bestFitBlockSize) / KMemoryManager.PageSize);
+ }
}
- pageList = null;
+ return address;
+ }
- return KernelResult.OutOfMemory;
+ public void FreePage(ulong address)
+ {
+ lock (_blocks)
+ {
+ FreePages(address, 1);
+ }
}
public void FreePages(KPageList pageList)
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs
index f2a05bda..6b92ed30 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs
@@ -4,7 +4,7 @@ using Ryujinx.HLE.HOS.Kernel.Process;
namespace Ryujinx.HLE.HOS.Kernel.Memory
{
- class KSharedMemory
+ class KSharedMemory : KAutoObject
{
private KPageList _pageList;
@@ -14,10 +14,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
private MemoryPermission _userPermission;
public KSharedMemory(
+ Horizon system,
KPageList pageList,
long ownerPid,
MemoryPermission ownerPermission,
- MemoryPermission userPermission)
+ MemoryPermission userPermission) : base(system)
{
_pageList = pageList;
_ownerPid = ownerPid;
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs
index 02367e89..a0929eca 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs
@@ -1,11 +1,13 @@
+using Ryujinx.HLE.HOS.Kernel.Common;
+
namespace Ryujinx.HLE.HOS.Kernel.Memory
{
- class KTransferMemory
+ class KTransferMemory : KAutoObject
{
public ulong Address { get; private set; }
public ulong Size { get; private set; }
- public KTransferMemory(ulong address, ulong size)
+ public KTransferMemory(Horizon system, ulong address, ulong size) : base(system)
{
Address = address;
Size = size;
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KHandleEntry.cs b/Ryujinx.HLE/HOS/Kernel/Process/KHandleEntry.cs
index 87137d0f..b5ca9b5e 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/KHandleEntry.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/KHandleEntry.cs
@@ -1,3 +1,5 @@
+using Ryujinx.HLE.HOS.Kernel.Common;
+
namespace Ryujinx.HLE.HOS.Kernel.Process
{
class KHandleEntry
@@ -6,8 +8,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public int Index { get; private set; }
- public ushort HandleId { get; set; }
- public object Obj { get; set; }
+ public ushort HandleId { get; set; }
+ public KAutoObject Obj { get; set; }
public KHandleEntry(int index)
{
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs b/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs
index 413edf94..88c2e690 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/KHandleTable.cs
@@ -6,8 +6,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
class KHandleTable
{
- private const int SelfThreadHandle = (0x1ffff << 15) | 0;
- private const int SelfProcessHandle = (0x1ffff << 15) | 1;
+ public const int SelfThreadHandle = (0x1ffff << 15) | 0;
+ public const int SelfProcessHandle = (0x1ffff << 15) | 1;
private Horizon _system;
@@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return KernelResult.Success;
}
- public KernelResult GenerateHandle(object obj, out int handle)
+ public KernelResult GenerateHandle(KAutoObject obj, out int handle)
{
handle = 0;
@@ -85,7 +85,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
_activeSlotsCount++;
- handle = (int)((_idCounter << 15) & 0xffff8000) | entry.Index;
+ handle = (_idCounter << 15) | entry.Index;
+
+ obj.IncrementReferenceCount();
if ((short)(_idCounter + 1) >= 0)
{
@@ -100,6 +102,72 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return KernelResult.Success;
}
+ public KernelResult ReserveHandle(out int handle)
+ {
+ handle = 0;
+
+ lock (_table)
+ {
+ if (_activeSlotsCount >= _size)
+ {
+ return KernelResult.HandleTableFull;
+ }
+
+ KHandleEntry entry = _nextFreeEntry;
+
+ _nextFreeEntry = entry.Next;
+
+ _activeSlotsCount++;
+
+ handle = (_idCounter << 15) | entry.Index;
+
+ if ((short)(_idCounter + 1) >= 0)
+ {
+ _idCounter++;
+ }
+ else
+ {
+ _idCounter = 1;
+ }
+ }
+
+ return KernelResult.Success;
+ }
+
+ public void CancelHandleReservation(int handle)
+ {
+ int index = (handle >> 0) & 0x7fff;
+ int handleId = (handle >> 15);
+
+ lock (_table)
+ {
+ KHandleEntry entry = _table[index];
+
+ entry.Obj = null;
+ entry.Next = _nextFreeEntry;
+
+ _nextFreeEntry = entry;
+
+ _activeSlotsCount--;
+ }
+ }
+
+ public void SetReservedHandleObj(int handle, KAutoObject obj)
+ {
+ int index = (handle >> 0) & 0x7fff;
+ int handleId = (handle >> 15);
+
+ lock (_table)
+ {
+ KHandleEntry entry = _table[index];
+
+ entry.Obj = obj;
+ entry.HandleId = (ushort)(handle >> 15);
+
+ obj.IncrementReferenceCount();
+ }
+ }
+
public bool CloseHandle(int handle)
{
if ((handle >> 30) != 0 ||
@@ -112,6 +180,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
int index = (handle >> 0) & 0x7fff;
int handleId = (handle >> 15);
+ KAutoObject obj = null;
+
bool result = false;
lock (_table)
@@ -120,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
KHandleEntry entry = _table[index];
- if (entry.Obj != null && entry.HandleId == handleId)
+ if ((obj = entry.Obj) != null && entry.HandleId == handleId)
{
entry.Obj = null;
entry.Next = _nextFreeEntry;
@@ -134,17 +204,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
}
}
+ if (result)
+ {
+ obj.DecrementReferenceCount();
+ }
+
return result;
}
- public T GetObject<T>(int handle)
+ public T GetObject<T>(int handle) where T : KAutoObject
{
int index = (handle >> 0) & 0x7fff;
int handleId = (handle >> 15);
lock (_table)
{
- if ((handle >> 30) == 0 && handleId != 0)
+ if ((handle >> 30) == 0 && handleId != 0 && index < _size)
{
KHandleEntry entry = _table[index];
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
index 0d77a495..855f3a18 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
@@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private SortedDictionary<ulong, KTlsPageInfo> _fullTlsPages;
private SortedDictionary<ulong, KTlsPageInfo> _freeTlsPages;
- public int DefaultCpuCore { get; private set; }
+ public int DefaultCpuCore { get; set; }
public bool Debug { get; private set; }
@@ -557,14 +557,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private KernelResult FreeTlsPage(KTlsPageInfo pageInfo)
{
- KernelResult result = MemoryManager.ConvertVaToPa(pageInfo.PageAddr, out ulong tlsPagePa);
-
- if (result != KernelResult.Success)
+ if (!MemoryManager.ConvertVaToPa(pageInfo.PageAddr, out ulong tlsPagePa))
{
throw new InvalidOperationException("Unexpected failure translating virtual address to physical.");
}
- result = MemoryManager.UnmapForKernel(pageInfo.PageAddr, 1, MemoryState.ThreadLocal);
+ KernelResult result = MemoryManager.UnmapForKernel(pageInfo.PageAddr, 1, MemoryState.ThreadLocal);
if (result == KernelResult.Success)
{
@@ -636,9 +634,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
void CleanUpForError()
{
- mainThread?.Terminate();
HandleTable.Destroy();
+ mainThread?.DecrementReferenceCount();
+
if (_mainThreadStackSize != 0)
{
ulong stackBottom = stackTop - _mainThreadStackSize;
@@ -646,6 +645,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
ulong stackPagesCount = _mainThreadStackSize / KMemoryManager.PageSize;
MemoryManager.UnmapForKernel(stackBottom, stackPagesCount, MemoryState.Stack);
+
+ _mainThreadStackSize = 0;
}
memoryResourceLimit?.Release(LimitableResource.Memory, stackSizeRounded);
@@ -756,6 +757,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
mainThread.Reschedule(ThreadSchedState.Running);
+ if (result == KernelResult.Success)
+ {
+ mainThread.IncrementReferenceCount();
+ }
+
+ mainThread.DecrementReferenceCount();
+
return result;
}
}
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs
index 033f0a2c..964762bb 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs
@@ -306,6 +306,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
int range = max - min + 1;
+ if (range == 64)
+ {
+ return -1L;
+ }
+
long mask = (1L << range) - 1;
return mask << min;
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs
index 08340b06..071b3c20 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs
@@ -1,10 +1,7 @@
using ChocolArm64.Events;
using ChocolArm64.Memory;
using ChocolArm64.State;
-using Ryujinx.HLE.HOS.Ipc;
-using Ryujinx.HLE.HOS.Kernel.Ipc;
using Ryujinx.HLE.HOS.Kernel.Process;
-using Ryujinx.HLE.HOS.Kernel.Threading;
using System;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
@@ -16,26 +13,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
private Horizon _system;
private MemoryManager _memory;
- private struct HleIpcMessage
- {
- public KThread Thread { get; private set; }
- public KSession Session { get; private set; }
- public IpcMessage Message { get; private set; }
- public long MessagePtr { get; private set; }
-
- public HleIpcMessage(
- KThread thread,
- KSession session,
- IpcMessage message,
- long messagePtr)
- {
- Thread = thread;
- Session = session;
- Message = message;
- MessagePtr = messagePtr;
- }
- }
-
public SvcHandler(Switch device, KProcess process)
{
_device = device;
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs
new file mode 100644
index 00000000..54939418
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs
@@ -0,0 +1,532 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
+{
+ partial class SvcHandler
+ {
+ private struct HleIpcMessage
+ {
+ public KThread Thread { get; private set; }
+ public KClientSession Session { get; private set; }
+ public IpcMessage Message { get; private set; }
+ public long MessagePtr { get; private set; }
+
+ public HleIpcMessage(
+ KThread thread,
+ KClientSession session,
+ IpcMessage message,
+ long messagePtr)
+ {
+ Thread = thread;
+ Session = session;
+ Message = message;
+ MessagePtr = messagePtr;
+ }
+ }
+
+ public KernelResult ConnectToNamedPort64(ulong namePtr, out int handle)
+ {
+ return ConnectToNamedPort(namePtr, out handle);
+ }
+
+ private KernelResult ConnectToNamedPort(ulong namePtr, out int handle)
+ {
+ handle = 0;
+
+ if (!KernelTransfer.UserToKernelString(_system, namePtr, 12, out string name))
+ {
+ return KernelResult.UserCopyFailed;
+ }
+
+ if (name.Length > 11)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ KAutoObject autoObj = KAutoObject.FindNamedObject(_system, name);
+
+ if (!(autoObj is KClientPort clientPort))
+ {
+ return KernelResult.NotFound;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KernelResult result = currentProcess.HandleTable.ReserveHandle(out handle);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ result = clientPort.Connect(out KClientSession clientSession);
+
+ if (result != KernelResult.Success)
+ {
+ currentProcess.HandleTable.CancelHandleReservation(handle);
+
+ return result;
+ }
+
+ currentProcess.HandleTable.SetReservedHandleObj(handle, clientSession);
+
+ clientSession.DecrementReferenceCount();
+
+ return result;
+ }
+
+ public KernelResult SendSyncRequest64(int handle)
+ {
+ return SendSyncRequest((ulong)_system.Scheduler.GetCurrentThread().Context.ThreadState.Tpidr, 0x100, handle);
+ }
+
+ public KernelResult SendSyncRequestWithUserBuffer64(ulong messagePtr, ulong size, int handle)
+ {
+ return SendSyncRequest(messagePtr, size, handle);
+ }
+
+ private KernelResult SendSyncRequest(ulong messagePtr, ulong size, int handle)
+ {
+ byte[] messageData = _memory.ReadBytes((long)messagePtr, (long)size);
+
+ KClientSession clientSession = _process.HandleTable.GetObject<KClientSession>(handle);
+
+ if (clientSession == null || clientSession.Service == null)
+ {
+ return SendSyncRequest_(handle);
+ }
+
+ if (clientSession != null)
+ {
+ _system.CriticalSection.Enter();
+
+ KThread currentThread = _system.Scheduler.GetCurrentThread();
+
+ currentThread.SignaledObj = null;
+ currentThread.ObjSyncResult = KernelResult.Success;
+
+ currentThread.Reschedule(ThreadSchedState.Paused);
+
+ IpcMessage message = new IpcMessage(messageData, (long)messagePtr);
+
+ ThreadPool.QueueUserWorkItem(ProcessIpcRequest, new HleIpcMessage(
+ currentThread,
+ clientSession,
+ message,
+ (long)messagePtr));
+
+ _system.ThreadCounter.AddCount();
+
+ _system.CriticalSection.Leave();
+
+ return currentThread.ObjSyncResult;
+ }
+ else
+ {
+ Logger.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{handle:x8}!");
+
+ return KernelResult.InvalidHandle;
+ }
+ }
+
+ private void ProcessIpcRequest(object state)
+ {
+ HleIpcMessage ipcMessage = (HleIpcMessage)state;
+
+ ipcMessage.Thread.ObjSyncResult = IpcHandler.IpcCall(
+ _device,
+ _process,
+ _memory,
+ ipcMessage.Session,
+ ipcMessage.Message,
+ ipcMessage.MessagePtr);
+
+ _system.ThreadCounter.Signal();
+
+ ipcMessage.Thread.Reschedule(ThreadSchedState.Running);
+ }
+
+ private KernelResult SendSyncRequest_(int handle)
+ {
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle);
+
+ if (session == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ return session.SendSyncRequest();
+ }
+
+ public KernelResult CreateSession64(
+ bool isLight,
+ ulong namePtr,
+ out int serverSessionHandle,
+ out int clientSessionHandle)
+ {
+ return CreateSession(isLight, namePtr, out serverSessionHandle, out clientSessionHandle);
+ }
+
+ private KernelResult CreateSession(
+ bool isLight,
+ ulong namePtr,
+ out int serverSessionHandle,
+ out int clientSessionHandle)
+ {
+ serverSessionHandle = 0;
+ clientSessionHandle = 0;
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KResourceLimit resourceLimit = currentProcess.ResourceLimit;
+
+ KernelResult result = KernelResult.Success;
+
+ if (resourceLimit != null && !resourceLimit.Reserve(LimitableResource.Session, 1))
+ {
+ return KernelResult.ResLimitExceeded;
+ }
+
+ if (isLight)
+ {
+ KLightSession session = new KLightSession(_system);
+
+ result = currentProcess.HandleTable.GenerateHandle(session.ServerSession, out serverSessionHandle);
+
+ if (result == KernelResult.Success)
+ {
+ result = currentProcess.HandleTable.GenerateHandle(session.ClientSession, out clientSessionHandle);
+
+ if (result != KernelResult.Success)
+ {
+ currentProcess.HandleTable.CloseHandle(serverSessionHandle);
+
+ serverSessionHandle = 0;
+ }
+ }
+
+ session.ServerSession.DecrementReferenceCount();
+ session.ClientSession.DecrementReferenceCount();
+ }
+ else
+ {
+ KSession session = new KSession(_system);
+
+ result = currentProcess.HandleTable.GenerateHandle(session.ServerSession, out serverSessionHandle);
+
+ if (result == KernelResult.Success)
+ {
+ result = currentProcess.HandleTable.GenerateHandle(session.ClientSession, out clientSessionHandle);
+
+ if (result != KernelResult.Success)
+ {
+ currentProcess.HandleTable.CloseHandle(serverSessionHandle);
+
+ serverSessionHandle = 0;
+ }
+ }
+
+ session.ServerSession.DecrementReferenceCount();
+ session.ClientSession.DecrementReferenceCount();
+ }
+
+ return result;
+ }
+
+ public KernelResult AcceptSession64(int portHandle, out int sessionHandle)
+ {
+ return AcceptSession(portHandle, out sessionHandle);
+ }
+
+ private KernelResult AcceptSession(int portHandle, out int sessionHandle)
+ {
+ sessionHandle = 0;
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KServerPort serverPort = currentProcess.HandleTable.GetObject<KServerPort>(portHandle);
+
+ if (serverPort == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ KernelResult result = currentProcess.HandleTable.ReserveHandle(out int handle);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ KAutoObject session;
+
+ if (serverPort.IsLight)
+ {
+ session = serverPort.AcceptIncomingLightConnection();
+ }
+ else
+ {
+ session = serverPort.AcceptIncomingConnection();
+ }
+
+ if (session != null)
+ {
+ currentProcess.HandleTable.SetReservedHandleObj(handle, session);
+
+ session.DecrementReferenceCount();
+
+ sessionHandle = handle;
+
+ result = KernelResult.Success;
+ }
+ else
+ {
+ currentProcess.HandleTable.CancelHandleReservation(handle);
+
+ result = KernelResult.NotFound;
+ }
+
+ return result;
+ }
+
+ public KernelResult ReplyAndReceive64(
+ ulong handlesPtr,
+ int handlesCount,
+ int replyTargetHandle,
+ long timeout,
+ out int handleIndex)
+ {
+ handleIndex = 0;
+
+ if ((uint)handlesCount > 0x40)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ ulong copySize = (ulong)((long)handlesCount * 4);
+
+ if (!currentProcess.MemoryManager.InsideAddrSpace(handlesPtr, copySize))
+ {
+ return KernelResult.UserCopyFailed;
+ }
+
+ if (handlesPtr + copySize < handlesPtr)
+ {
+ return KernelResult.UserCopyFailed;
+ }
+
+ int[] handles = new int[handlesCount];
+
+ if (!KernelTransfer.UserToKernelInt32Array(_system, handlesPtr, handles))
+ {
+ 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 KernelResult.InvalidHandle;
+ }
+
+ syncObjs[index] = obj;
+ }
+
+ KernelResult result;
+
+ if (replyTargetHandle != 0)
+ {
+ KServerSession replyTarget = currentProcess.HandleTable.GetObject<KServerSession>(replyTargetHandle);
+
+ if (replyTarget == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ result = replyTarget.Reply();
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+ }
+
+ while ((result = _system.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 CreatePort64(
+ int maxSessions,
+ bool isLight,
+ ulong namePtr,
+ out int serverPortHandle,
+ out int clientPortHandle)
+ {
+ return CreatePort(maxSessions, isLight, namePtr, out serverPortHandle, out clientPortHandle);
+ }
+
+ private KernelResult CreatePort(
+ int maxSessions,
+ bool isLight,
+ ulong namePtr,
+ out int serverPortHandle,
+ out int clientPortHandle)
+ {
+ serverPortHandle = clientPortHandle = 0;
+
+ if (maxSessions < 1)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ KPort port = new KPort(_system, maxSessions, isLight, (long)namePtr);
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ClientPort, out clientPortHandle);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out serverPortHandle);
+
+ if (result != KernelResult.Success)
+ {
+ currentProcess.HandleTable.CloseHandle(clientPortHandle);
+ }
+
+ return result;
+ }
+
+ public KernelResult ManageNamedPort64(ulong namePtr, int maxSessions, out int handle)
+ {
+ return ManageNamedPort(namePtr, maxSessions, out handle);
+ }
+
+ private KernelResult ManageNamedPort(ulong namePtr, int maxSessions, out int handle)
+ {
+ handle = 0;
+
+ if (!KernelTransfer.UserToKernelString(_system, namePtr, 12, out string name))
+ {
+ return KernelResult.UserCopyFailed;
+ }
+
+ if (maxSessions < 0 || name.Length > 11)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ if (maxSessions == 0)
+ {
+ return KClientPort.RemoveName(_system, name);
+ }
+
+ KPort port = new KPort(_system, maxSessions, false, 0);
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out handle);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ result = port.ClientPort.SetName(name);
+
+ if (result != KernelResult.Success)
+ {
+ currentProcess.HandleTable.CloseHandle(handle);
+ }
+
+ return result;
+ }
+
+ public KernelResult ConnectToPort64(int clientPortHandle, out int clientSessionHandle)
+ {
+ return ConnectToPort(clientPortHandle, out clientSessionHandle);
+ }
+
+ private KernelResult ConnectToPort(int clientPortHandle, out int clientSessionHandle)
+ {
+ clientSessionHandle = 0;
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KClientPort clientPort = currentProcess.HandleTable.GetObject<KClientPort>(clientPortHandle);
+
+ if (clientPort == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ KernelResult result = currentProcess.HandleTable.ReserveHandle(out int handle);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ KAutoObject session;
+
+ if (clientPort.IsLight)
+ {
+ result = clientPort.ConnectLight(out KLightClientSession clientSession);
+
+ session = clientSession;
+ }
+ else
+ {
+ result = clientPort.Connect(out KClientSession clientSession);
+
+ session = clientSession;
+ }
+
+ if (result != KernelResult.Success)
+ {
+ currentProcess.HandleTable.CancelHandleReservation(handle);
+
+ return result;
+ }
+
+ currentProcess.HandleTable.SetReservedHandleObj(handle, session);
+
+ session.DecrementReferenceCount();
+
+ clientSessionHandle = handle;
+
+ return result;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs
index e6590522..388dcc21 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs
@@ -305,7 +305,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
- KTransferMemory transferMemory = new KTransferMemory(address, size);
+ KTransferMemory transferMemory = new KTransferMemory(_system, address, size);
return _process.HandleTable.GenerateHandle(transferMemory, out handle);
}
@@ -350,7 +350,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public KernelResult UnmapPhysicalMemory64(ulong address, ulong size)
{
- return MapPhysicalMemory(address, size);
+ return UnmapPhysicalMemory(address, size);
}
private KernelResult UnmapPhysicalMemory(ulong address, ulong size)
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs
index b0563356..be136ff0 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs
@@ -2,14 +2,11 @@ using ChocolArm64.Memory;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
-using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Ipc;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
-using Ryujinx.HLE.HOS.Services;
-using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
@@ -82,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
private KernelResult CloseHandle(int handle)
{
- object obj = _process.HandleTable.GetObject<object>(handle);
+ KAutoObject obj = _process.HandleTable.GetObject<KAutoObject>(handle);
_process.HandleTable.CloseHandle(handle);
@@ -144,88 +141,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return _system.Scheduler.GetCurrentThread().Context.ThreadState.CntpctEl0;
}
- public KernelResult ConnectToNamedPort64(ulong namePtr, out int handle)
- {
- return ConnectToNamedPort(namePtr, out handle);
- }
-
- private KernelResult ConnectToNamedPort(ulong namePtr, out int handle)
- {
- string name = MemoryHelper.ReadAsciiString(_memory, (long)namePtr, 8);
-
- //TODO: Validate that app has perms to access the service, and that the service
- //actually exists, return error codes otherwise.
- KSession session = new KSession(ServiceFactory.MakeService(_system, name), name);
-
- return _process.HandleTable.GenerateHandle(session, out handle);
- }
-
- public KernelResult SendSyncRequest64(int handle)
- {
- return SendSyncRequest((ulong)_system.Scheduler.GetCurrentThread().Context.ThreadState.Tpidr, 0x100, handle);
- }
-
- public KernelResult SendSyncRequestWithUserBuffer64(ulong messagePtr, ulong size, int handle)
- {
- return SendSyncRequest(messagePtr, size, handle);
- }
-
- private KernelResult SendSyncRequest(ulong messagePtr, ulong size, int handle)
- {
- byte[] messageData = _memory.ReadBytes((long)messagePtr, (long)size);
-
- KSession session = _process.HandleTable.GetObject<KSession>(handle);
-
- if (session != null)
- {
- _system.CriticalSection.Enter();
-
- KThread currentThread = _system.Scheduler.GetCurrentThread();
-
- currentThread.SignaledObj = null;
- currentThread.ObjSyncResult = KernelResult.Success;
-
- currentThread.Reschedule(ThreadSchedState.Paused);
-
- IpcMessage message = new IpcMessage(messageData, (long)messagePtr);
-
- ThreadPool.QueueUserWorkItem(ProcessIpcRequest, new HleIpcMessage(
- currentThread,
- session,
- message,
- (long)messagePtr));
-
- _system.ThreadCounter.AddCount();
-
- _system.CriticalSection.Leave();
-
- return currentThread.ObjSyncResult;
- }
- else
- {
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{handle:x8}!");
-
- return KernelResult.InvalidHandle;
- }
- }
-
- private void ProcessIpcRequest(object state)
- {
- HleIpcMessage ipcMessage = (HleIpcMessage)state;
-
- ipcMessage.Thread.ObjSyncResult = IpcHandler.IpcCall(
- _device,
- _process,
- _memory,
- ipcMessage.Session,
- ipcMessage.Message,
- ipcMessage.MessagePtr);
-
- _system.ThreadCounter.Signal();
-
- ipcMessage.Thread.Reschedule(ThreadSchedState.Running);
- }
-
public KernelResult GetProcessId64(int handle, out long pid)
{
return GetProcessId(handle, out pid);
@@ -664,99 +579,5 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.Success;
}
-
- public KernelResult CreatePort64(
- int maxSessions,
- bool isLight,
- ulong namePtr,
- out int serverPortHandle,
- out int clientPortHandle)
- {
- return CreatePort(maxSessions, isLight, namePtr, out serverPortHandle, out clientPortHandle);
- }
-
- private KernelResult CreatePort(
- int maxSessions,
- bool isLight,
- ulong namePtr,
- out int serverPortHandle,
- out int clientPortHandle)
- {
- serverPortHandle = clientPortHandle = 0;
-
- if (maxSessions < 1)
- {
- return KernelResult.MaximumExceeded;
- }
-
- KPort port = new KPort(_system);
-
- port.Initialize(maxSessions, isLight, (long)namePtr);
-
- KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
-
- KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ClientPort, out clientPortHandle);
-
- if (result != KernelResult.Success)
- {
- return result;
- }
-
- result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out serverPortHandle);
-
- if (result != KernelResult.Success)
- {
- currentProcess.HandleTable.CloseHandle(clientPortHandle);
- }
-
- return result;
- }
-
- public KernelResult ManageNamedPort64(ulong namePtr, int maxSessions, out int handle)
- {
- return ManageNamedPort(namePtr, maxSessions, out handle);
- }
-
- private KernelResult ManageNamedPort(ulong namePtr, int maxSessions, out int handle)
- {
- handle = 0;
-
- if (!KernelTransfer.UserToKernelString(_system, namePtr, 12, out string name))
- {
- return KernelResult.UserCopyFailed;
- }
-
- if (maxSessions < 0 || name.Length > 11)
- {
- return KernelResult.MaximumExceeded;
- }
-
- if (maxSessions == 0)
- {
- return KClientPort.RemoveName(_system, name);
- }
-
- KPort port = new KPort(_system);
-
- KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
-
- KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out handle);
-
- if (result != KernelResult.Success)
- {
- return result;
- }
-
- port.Initialize(maxSessions, false, 0);
-
- result = port.SetName(name);
-
- if (result != KernelResult.Success)
- {
- currentProcess.HandleTable.CloseHandle(handle);
- }
-
- return result;
- }
}
}
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs
index a6111777..cbcb712f 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs
@@ -63,11 +63,15 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{ 0x33, nameof(SvcHandler.GetThreadContext364) },
{ 0x34, nameof(SvcHandler.WaitForAddress64) },
{ 0x35, nameof(SvcHandler.SignalToAddress64) },
+ { 0x40, nameof(SvcHandler.CreateSession64) },
+ { 0x41, nameof(SvcHandler.AcceptSession64) },
+ { 0x43, nameof(SvcHandler.ReplyAndReceive64) },
{ 0x45, nameof(SvcHandler.CreateEvent64) },
{ 0x65, nameof(SvcHandler.GetProcessList64) },
{ 0x6f, nameof(SvcHandler.GetSystemInfo64) },
{ 0x70, nameof(SvcHandler.CreatePort64) },
- { 0x71, nameof(SvcHandler.ManageNamedPort64) }
+ { 0x71, nameof(SvcHandler.ManageNamedPort64) },
+ { 0x72, nameof(SvcHandler.ConnectToPort64) }
};
_svcTable64 = new Action<SvcHandler, CpuThreadState>[0x80];
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs
index 1e1927fe..64268ff2 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs
@@ -62,22 +62,17 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
priority,
cpuCore);
- if (result != KernelResult.Success)
+ if (result == KernelResult.Success)
{
- currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
-
- return result;
+ result = _process.HandleTable.GenerateHandle(thread, out handle);
}
-
- result = _process.HandleTable.GenerateHandle(thread, out handle);
-
- if (result != KernelResult.Success)
+ else
{
- thread.Terminate();
-
currentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
}
+ thread.DecrementReferenceCount();
+
return result;
}
@@ -88,11 +83,22 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
private KernelResult StartThread(int handle)
{
- KThread thread = _process.HandleTable.GetObject<KThread>(handle);
+ KThread thread = _process.HandleTable.GetKThread(handle);
if (thread != null)
{
- return thread.Start();
+ thread.IncrementReferenceCount();
+
+ KernelResult result = thread.Start();
+
+ if (result == KernelResult.Success)
+ {
+ thread.IncrementReferenceCount();
+ }
+
+ thread.DecrementReferenceCount();
+
+ return result;
}
else
{
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs
index 5bdb9c1d..dd982291 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KEvent.cs
@@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KEvent(Horizon system)
{
ReadableEvent = new KReadableEvent(system, this);
- WritableEvent = new KWritableEvent(this);
+ WritableEvent = new KWritableEvent(system, this);
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
index 60e15efa..c9686df3 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
@@ -210,9 +210,29 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
}
}
+ return GetDummyThread();
+
throw new InvalidOperationException("Current thread is not scheduled!");
}
+ private KThread _dummyThread;
+
+ private KThread GetDummyThread()
+ {
+ if (_dummyThread != null)
+ {
+ return _dummyThread;
+ }
+
+ KProcess dummyProcess = new KProcess(_system);
+
+ KThread dummyThread = new KThread(_system);
+
+ dummyThread.Initialize(0, 0, 0, 44, 0, dummyProcess, ThreadType.Dummy);
+
+ return _dummyThread = dummyThread;
+ }
+
public KProcess GetCurrentProcess()
{
return GetCurrentThread().Owner;
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
index 450155ce..327b0418 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
@@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
_system.CriticalSection.Leave();
- return 0;
+ return KernelResult.Success;
}
if (timeout == 0)
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
index 3ad64024..302e8f41 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
@@ -30,6 +30,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private ulong _tlsAddress;
+ public ulong TlsAddress => _tlsAddress;
+ public ulong TlsDramAddress { get; private set; }
+
public long LastScheduledTime { get; set; }
public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
@@ -67,6 +70,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public bool WaitingSync { get; set; }
private bool _hasExited;
+ private bool _hasBeenInitialized;
+ private bool _hasBeenReleased;
public bool WaitingInArbitration { get; set; }
@@ -124,6 +129,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return KernelResult.OutOfMemory;
}
+ TlsDramAddress = owner.MemoryManager.GetDramAddressFromVa(_tlsAddress);
+
MemoryHelper.FillWithZeros(owner.CpuMemory, (long)_tlsAddress, KTlsPageInfo.TlsEntrySize);
}
@@ -133,6 +140,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
Owner = owner;
+ owner.IncrementReferenceCount();
owner.IncrementThreadCount();
is64Bits = (owner.MmuFlags & 1) != 0;
@@ -156,6 +164,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
ThreadUid = System.GetThreadUid();
+ _hasBeenInitialized = true;
+
if (owner != null)
{
owner.AddThread(this);
@@ -252,6 +262,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public void Exit()
{
+ //TODO: Debug event.
+
+ if (Owner != null)
+ {
+ Owner.ResourceLimit?.Release(LimitableResource.Thread, 0, 1);
+
+ _hasBeenReleased = true;
+ }
+
System.CriticalSection.Enter();
_forcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
@@ -259,6 +278,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
ExitImpl();
System.CriticalSection.Leave();
+
+ DecrementReferenceCount();
}
private void ExitImpl()
@@ -930,7 +951,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
return;
}
- //Remove from old queues.
+ //Remove thread from the old priority queues.
for (int core = 0; core < KScheduler.CpuCoresCount; core++)
{
if (((oldAffinityMask >> core) & 1) != 0)
@@ -946,7 +967,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
}
}
- //Insert on new queues.
+ //Add thread to the new priority queues.
for (int core = 0; core < KScheduler.CpuCoresCount; core++)
{
if (((AffinityMask >> core) & 1) != 0)
@@ -965,11 +986,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
_scheduler.ThreadReselectionRequested = true;
}
- public override bool IsSignaled()
- {
- return _hasExited;
- }
-
public void SetEntryArguments(long argsPtr, int threadHandle)
{
Context.ThreadState.X0 = (ulong)argsPtr;
@@ -994,13 +1010,36 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private void ThreadFinishedHandler(object sender, EventArgs e)
{
System.Scheduler.ExitThread(this);
+ System.Scheduler.RemoveThread(this);
+ }
- Terminate();
+ public override bool IsSignaled()
+ {
+ return _hasExited;
+ }
- System.Scheduler.RemoveThread(this);
+ protected override void Destroy()
+ {
+ if (_hasBeenInitialized)
+ {
+ FreeResources();
+
+ bool released = Owner != null || _hasBeenReleased;
+
+ if (Owner != null)
+ {
+ Owner.ResourceLimit?.Release(LimitableResource.Thread, 1, released ? 0 : 1);
+
+ Owner.DecrementReferenceCount();
+ }
+ else
+ {
+ System.ResourceLimit.Release(LimitableResource.Thread, 1, released ? 0 : 1);
+ }
+ }
}
- public void Terminate()
+ private void FreeResources()
{
Owner?.RemoveThread(this);
@@ -1011,8 +1050,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
System.CriticalSection.Enter();
- //Wake up all threads that may be waiting for a mutex being held
- //by this thread.
+ //Wake up all threads that may be waiting for a mutex being held by this thread.
foreach (KThread thread in _mutexWaiters)
{
thread.MutexOwner = null;
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs
index c9b2f40d..1db88995 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KWritableEvent.cs
@@ -2,11 +2,11 @@ using Ryujinx.HLE.HOS.Kernel.Common;
namespace Ryujinx.HLE.HOS.Kernel.Threading
{
- class KWritableEvent
+ class KWritableEvent : KAutoObject
{
private KEvent _parent;
- public KWritableEvent(KEvent parent)
+ public KWritableEvent(Horizon system, KEvent parent) : base(system)
{
_parent = parent;
}
diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs
index bb09db6e..568c56ef 100644
--- a/Ryujinx.HLE/HOS/ProgramLoader.cs
+++ b/Ryujinx.HLE/HOS/ProgramLoader.cs
@@ -63,11 +63,11 @@ namespace Ryujinx.HLE.HOS
0,
0);
- MemoryRegion memRegion = kip.IsService
+ MemoryRegion memoryRegion = kip.IsService
? MemoryRegion.Service
: MemoryRegion.Application;
- KMemoryRegionManager region = system.MemoryRegions[(int)memRegion];
+ KMemoryRegionManager region = system.MemoryRegions[(int)memoryRegion];
KernelResult result = region.AllocatePages((ulong)codePagesCount, false, out KPageList pageList);
@@ -85,7 +85,7 @@ namespace Ryujinx.HLE.HOS
kip.Capabilities,
pageList,
system.ResourceLimit,
- memRegion);
+ memoryRegion);
if (result != KernelResult.Success)
{
@@ -103,6 +103,8 @@ namespace Ryujinx.HLE.HOS
return false;
}
+ process.DefaultCpuCore = kip.DefaultProcessorId;
+
result = process.Start(kip.MainThreadPriority, (ulong)kip.MainThreadStackSize);
if (result != KernelResult.Success)
@@ -201,11 +203,20 @@ namespace Ryujinx.HLE.HOS
KProcess process = new KProcess(system);
+ MemoryRegion memoryRegion = (MemoryRegion)((metaData.Acid.Flags >> 2) & 0xf);
+
+ if (memoryRegion > MemoryRegion.NvServices)
+ {
+ Logger.PrintError(LogClass.Loader, $"Process initialization failed due to invalid ACID flags.");
+
+ return false;
+ }
+
result = process.Initialize(
creationInfo,
metaData.Aci0.KernelAccessControl.Capabilities,
resourceLimit,
- MemoryRegion.Application);
+ memoryRegion);
if (result != KernelResult.Success)
{
@@ -228,6 +239,8 @@ namespace Ryujinx.HLE.HOS
}
}
+ process.DefaultCpuCore = metaData.DefaultCpuId;
+
result = process.Start(metaData.MainThreadPriority, (ulong)metaData.MainThreadStackSize);
if (result != KernelResult.Success)
diff --git a/Ryujinx.HLE/HOS/ServiceCtx.cs b/Ryujinx.HLE/HOS/ServiceCtx.cs
index 005d16f3..af42d417 100644
--- a/Ryujinx.HLE/HOS/ServiceCtx.cs
+++ b/Ryujinx.HLE/HOS/ServiceCtx.cs
@@ -8,24 +8,24 @@ namespace Ryujinx.HLE.HOS
{
class ServiceCtx
{
- public Switch Device { get; private set; }
- public KProcess Process { get; private set; }
- public MemoryManager Memory { get; private set; }
- public KSession Session { get; private set; }
- public IpcMessage Request { get; private set; }
- public IpcMessage Response { get; private set; }
- public BinaryReader RequestData { get; private set; }
- public BinaryWriter ResponseData { get; private set; }
+ public Switch Device { get; }
+ public KProcess Process { get; }
+ public MemoryManager Memory { get; }
+ public KClientSession Session { get; }
+ public IpcMessage Request { get; }
+ public IpcMessage Response { get; }
+ public BinaryReader RequestData { get; }
+ public BinaryWriter ResponseData { get; }
public ServiceCtx(
- Switch device,
- KProcess process,
- MemoryManager memory,
- KSession session,
- IpcMessage request,
- IpcMessage response,
- BinaryReader requestData,
- BinaryWriter responseData)
+ Switch device,
+ KProcess process,
+ MemoryManager memory,
+ KClientSession session,
+ IpcMessage request,
+ IpcMessage response,
+ BinaryReader requestData,
+ BinaryWriter responseData)
{
Device = device;
Process = process;
diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs
index 5c7fa18d..71683ce3 100644
--- a/Ryujinx.HLE/HOS/Services/IpcService.cs
+++ b/Ryujinx.HLE/HOS/Services/IpcService.cs
@@ -116,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Services
}
else
{
- string dbgMessage = $"{context.Session.ServiceName} {service.GetType().Name}: {commandId}";
+ string dbgMessage = $"{service.GetType().FullName}: {commandId}";
throw new ServiceNotImplementedException(context, dbgMessage);
}
@@ -132,9 +132,11 @@ namespace Ryujinx.HLE.HOS.Services
}
else
{
- KSession session = new KSession(obj, context.Session.ServiceName);
+ KSession session = new KSession(context.Device.System);
- if (context.Process.HandleTable.GenerateHandle(session, out int handle) != KernelResult.Success)
+ session.ClientSession.Service = obj;
+
+ if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
@@ -151,7 +153,7 @@ namespace Ryujinx.HLE.HOS.Services
{
int handle = context.Request.HandleDesc.ToMove[index];
- KSession session = context.Process.HandleTable.GetObject<KSession>(handle);
+ KClientSession session = context.Process.HandleTable.GetObject<KClientSession>(handle);
return session?.Service is T ? (T)session.Service : null;
}
diff --git a/Ryujinx.HLE/HOS/Services/Psm/IPsmSession.cs b/Ryujinx.HLE/HOS/Services/Psm/IPsmSession.cs
index aeeeb052..3db3bd27 100644
--- a/Ryujinx.HLE/HOS/Services/Psm/IPsmSession.cs
+++ b/Ryujinx.HLE/HOS/Services/Psm/IPsmSession.cs
@@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Psm
{
if (_stateChangeEventHandle == -1)
{
- KernelResult resultCode = context.Process.HandleTable.GenerateHandle(_stateChangeEvent, out int stateChangeEventHandle);
+ KernelResult resultCode = context.Process.HandleTable.GenerateHandle(_stateChangeEvent.ReadableEvent, out int stateChangeEventHandle);
if (resultCode != KernelResult.Success)
{
diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
index df551a41..6940bfc8 100644
--- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
+++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
@@ -1,8 +1,11 @@
+using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Ipc;
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.IO;
namespace Ryujinx.HLE.HOS.Services.Sm
{
@@ -12,18 +15,30 @@ namespace Ryujinx.HLE.HOS.Services.Sm
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
+ private ConcurrentDictionary<string, KPort> _registeredServices;
+
private bool _isInitialized;
public IUserInterface()
{
_commands = new Dictionary<int, ServiceProcessRequest>
{
- { 0, Initialize },
- { 1, GetService }
+ { 0, Initialize },
+ { 1, GetService },
+ { 2, RegisterService }
};
+
+ _registeredServices = new ConcurrentDictionary<string, KPort>();
}
- private const int SmNotInitialized = 0x415;
+ public static void InitializePort(Horizon system)
+ {
+ KPort port = new KPort(system, 256, false, 0);
+
+ port.ClientPort.SetName("sm:");
+
+ port.ClientPort.Service = new IUserInterface();
+ }
public long Initialize(ServiceCtx context)
{
@@ -34,34 +49,76 @@ namespace Ryujinx.HLE.HOS.Services.Sm
public long GetService(ServiceCtx context)
{
- //Only for kernel version > 3.0.0.
if (!_isInitialized)
{
- //return SmNotInitialized;
+ return ErrorCode.MakeError(ErrorModule.Sm, SmErr.NotInitialized);
}
- string name = string.Empty;
+ string name = ReadName(context);
- for (int index = 0; index < 8 &&
- context.RequestData.BaseStream.Position <
- context.RequestData.BaseStream.Length; index++)
+ if (name == string.Empty)
{
- byte chr = context.RequestData.ReadByte();
+ return ErrorCode.MakeError(ErrorModule.Sm, SmErr.InvalidName);
+ }
- if (chr >= 0x20 && chr < 0x7f)
+ KSession session = new KSession(context.Device.System);
+
+ if (_registeredServices.TryGetValue(name, out KPort port))
+ {
+ KernelResult result = port.EnqueueIncomingSession(session.ServerSession);
+
+ if (result != KernelResult.Success)
{
- name += (char)chr;
+ throw new InvalidOperationException($"Session enqueue on port returned error \"{result}\".");
}
}
+ else
+ {
+ session.ClientSession.Service = ServiceFactory.MakeService(context.Device.System, name);
+ }
+
+ if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Out of handles!");
+ }
+
+ context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
+
+ return 0;
+ }
+
+ public long RegisterService(ServiceCtx context)
+ {
+ if (!_isInitialized)
+ {
+ return ErrorCode.MakeError(ErrorModule.Sm, SmErr.NotInitialized);
+ }
+
+ long namePosition = context.RequestData.BaseStream.Position;
+
+ string name = ReadName(context);
+
+ context.RequestData.BaseStream.Seek(namePosition + 8, SeekOrigin.Begin);
+
+ bool isLight = (context.RequestData.ReadInt32() & 1) != 0;
+
+ int maxSessions = context.RequestData.ReadInt32();
if (name == string.Empty)
{
- return 0;
+ return ErrorCode.MakeError(ErrorModule.Sm, SmErr.InvalidName);
}
- KSession session = new KSession(ServiceFactory.MakeService(context.Device.System, name), name);
+ Logger.PrintInfo(LogClass.ServiceSm, $"Register \"{name}\".");
+
+ KPort port = new KPort(context.Device.System, maxSessions, isLight, 0);
- if (context.Process.HandleTable.GenerateHandle(session, out int handle) != KernelResult.Success)
+ if (!_registeredServices.TryAdd(name, port))
+ {
+ return ErrorCode.MakeError(ErrorModule.Sm, SmErr.AlreadyRegistered);
+ }
+
+ if (context.Process.HandleTable.GenerateHandle(port.ServerPort, out int handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
@@ -70,5 +127,24 @@ namespace Ryujinx.HLE.HOS.Services.Sm
return 0;
}
+
+ private static string ReadName(ServiceCtx context)
+ {
+ string name = string.Empty;
+
+ for (int index = 0; index < 8 &&
+ context.RequestData.BaseStream.Position <
+ context.RequestData.BaseStream.Length; index++)
+ {
+ byte chr = context.RequestData.ReadByte();
+
+ if (chr >= 0x20 && chr < 0x7f)
+ {
+ name += (char)chr;
+ }
+ }
+
+ return name;
+ }
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Sm/SmErr.cs b/Ryujinx.HLE/HOS/Services/Sm/SmErr.cs
new file mode 100644
index 00000000..5b5a66dc
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Sm/SmErr.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.HOS.Services.Sm
+{
+ static class SmErr
+ {
+ public const int NotInitialized = 2;
+ public const int AlreadyRegistered = 4;
+ public const int InvalidName = 6;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs b/Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs
index 166ae60a..0a76dc95 100644
--- a/Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs
+++ b/Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs
@@ -1,5 +1,4 @@
using System;
-using System.IO;
namespace Ryujinx.HLE.Loaders.Compression
{
@@ -7,22 +6,26 @@ namespace Ryujinx.HLE.Loaders.Compression
{
private class BackwardsReader
{
- private Stream _baseStream;
+ private byte[] _data;
- public BackwardsReader(Stream baseStream)
+ private int _position;
+
+ public int Position => _position;
+
+ public BackwardsReader(byte[] data, int end)
{
- _baseStream = baseStream;
+ _data = data;
+ _position = end;
}
- public byte ReadByte()
+ public void SeekCurrent(int offset)
{
- _baseStream.Seek(-1, SeekOrigin.Current);
-
- byte value = (byte)_baseStream.ReadByte();
-
- _baseStream.Seek(-1, SeekOrigin.Current);
+ _position += offset;
+ }
- return value;
+ public byte ReadByte()
+ {
+ return _data[--_position];
}
public short ReadInt16()
@@ -39,30 +42,24 @@ namespace Ryujinx.HLE.Loaders.Compression
}
}
- public static byte[] Decompress(Stream input, int decompressedLength)
+ public static void DecompressInPlace(byte[] buffer, int headerEnd)
{
- long end = input.Position;
-
- BackwardsReader reader = new BackwardsReader(input);
+ BackwardsReader reader = new BackwardsReader(buffer, headerEnd);
int additionalDecLength = reader.ReadInt32();
int startOffset = reader.ReadInt32();
int compressedLength = reader.ReadInt32();
- input.Seek(12 - startOffset, SeekOrigin.Current);
-
- byte[] dec = new byte[decompressedLength];
+ reader.SeekCurrent(12 - startOffset);
- int decompressedLengthUnpadded = compressedLength + additionalDecLength;
+ int decBase = headerEnd - compressedLength;
- int decompressionStart = decompressedLength - decompressedLengthUnpadded;
-
- int decPos = dec.Length;
+ int decPos = compressedLength + additionalDecLength;
byte mask = 0;
byte header = 0;
- while (decPos > decompressionStart)
+ while (decPos > 0)
{
if ((mask >>= 1) == 0)
{
@@ -72,7 +69,7 @@ namespace Ryujinx.HLE.Loaders.Compression
if ((header & mask) == 0)
{
- dec[--decPos] = reader.ReadByte();
+ buffer[decBase + --decPos] = reader.ReadByte();
}
else
{
@@ -81,25 +78,30 @@ namespace Ryujinx.HLE.Loaders.Compression
int length = (pair >> 12) + 3;
int position = (pair & 0xfff) + 3;
+ if (length > decPos)
+ {
+ length = decPos;
+ }
+
decPos -= length;
+ int dstPos = decBase + decPos;
+
if (length <= position)
{
- int srcPos = decPos + position;
+ int srcPos = dstPos + position;
- Buffer.BlockCopy(dec, srcPos, dec, decPos, length);
+ Buffer.BlockCopy(buffer, srcPos, buffer, dstPos, length);
}
else
{
for (int offset = 0; offset < length; offset++)
{
- dec[decPos + offset] = dec[decPos + position + offset];
+ buffer[dstPos + offset] = buffer[dstPos + position + offset];
}
}
}
}
-
- return dec;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs b/Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs
index 8b09bfcd..af57cf2d 100644
--- a/Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs
+++ b/Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs
@@ -98,7 +98,7 @@ namespace Ryujinx.HLE.Loaders.Executables
MainThreadStackSize = segments[1].Attribute;
- Capabilities = new int[8];
+ Capabilities = new int[32];
for (int index = 0; index < Capabilities.Length; index++)
{
@@ -114,13 +114,11 @@ namespace Ryujinx.HLE.Loaders.Executables
private byte[] ReadSegment(SegmentHeader header, Stream input)
{
- long end = input.Position + header.CompressedSize;
+ byte[] data = new byte[header.DecompressedSize];
- input.Seek(end, SeekOrigin.Begin);
+ input.Read(data, 0, header.CompressedSize);
- byte[] data = BackwardsLz.Decompress(input, header.DecompressedSize);
-
- input.Seek(end, SeekOrigin.Begin);
+ BackwardsLz.DecompressInPlace(data, header.CompressedSize);
return data;
}
diff --git a/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs b/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs
index 368dbae7..def780a2 100644
--- a/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs
+++ b/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs
@@ -31,7 +31,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
int length = (controlByte & 0x07) + 1;
bool registerAllowed = (controlByte & 0x80) != 0;
- services.Add(Encoding.ASCII.GetString(reader.ReadBytes(length), 0, length), registerAllowed);
+ services[Encoding.ASCII.GetString(reader.ReadBytes(length))] = registerAllowed;
byteReaded += length + 1;
}