diff options
author | gdkchan <gab.dark.100@gmail.com> | 2019-01-18 20:26:39 -0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-18 20:26:39 -0200 |
commit | 22bacc618815170c0d186a82e1ea4558e36b7063 (patch) | |
tree | 79b97959481fea1ac301da6d4e9dea9b991ece6f /Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs | |
parent | 3731d0ce8412c3c48286c242842bcb4940b4ca6d (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.cs | 216 |
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 |