aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2019-01-18 20:26:39 -0200
committerGitHub <noreply@github.com>2019-01-18 20:26:39 -0200
commit22bacc618815170c0d186a82e1ea4558e36b7063 (patch)
tree79b97959481fea1ac301da6d4e9dea9b991ece6f /Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs
parent3731d0ce8412c3c48286c242842bcb4940b4ca6d (diff)
Improve kernel IPC implementation (#550)
* Implement some IPC related kernel SVCs properly * Fix BLZ decompression when the segment also has a uncompressed chunck * Set default cpu core on process start from ProgramLoader, remove debug message * Load process capabilities properly on KIPs * Fix a copy/paste error in UnmapPhysicalMemory64 * Implement smarter switching between old and new IPC system to support the old HLE services implementation without the manual switch * Implement RegisterService on sm and AcceptSession (partial) * Misc fixes and improvements on new IPC methods * Move IPC related SVCs into a separate file, and logging on RegisterService (sm) * Some small fixes related to receive list buffers and error cases * Load NSOs using the correct pool partition * Fix corner case on GetMaskFromMinMax where range is 64, doesn't happen in pratice however * Fix send static buffer copy * Session release, implement closing requests on client disconnect * Implement ConnectToPort SVC * KLightSession init
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs')
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs216
1 files changed, 216 insertions, 0 deletions
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