KBufferDescriptorTable.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. using Ryujinx.Common;
  2. using Ryujinx.HLE.HOS.Kernel.Common;
  3. using Ryujinx.HLE.HOS.Kernel.Memory;
  4. using System.Collections.Generic;
  5. namespace Ryujinx.HLE.HOS.Kernel.Ipc
  6. {
  7. class KBufferDescriptorTable
  8. {
  9. private const int MaxInternalBuffersCount = 8;
  10. private List<KBufferDescriptor> _sendBufferDescriptors;
  11. private List<KBufferDescriptor> _receiveBufferDescriptors;
  12. private List<KBufferDescriptor> _exchangeBufferDescriptors;
  13. public KBufferDescriptorTable()
  14. {
  15. _sendBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount);
  16. _receiveBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount);
  17. _exchangeBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount);
  18. }
  19. public KernelResult AddSendBuffer(ulong src, ulong dst, ulong size, MemoryState state)
  20. {
  21. return Add(_sendBufferDescriptors, src, dst, size, state);
  22. }
  23. public KernelResult AddReceiveBuffer(ulong src, ulong dst, ulong size, MemoryState state)
  24. {
  25. return Add(_receiveBufferDescriptors, src, dst, size, state);
  26. }
  27. public KernelResult AddExchangeBuffer(ulong src, ulong dst, ulong size, MemoryState state)
  28. {
  29. return Add(_exchangeBufferDescriptors, src, dst, size, state);
  30. }
  31. private KernelResult Add(List<KBufferDescriptor> list, ulong src, ulong dst, ulong size, MemoryState state)
  32. {
  33. if (list.Count < MaxInternalBuffersCount)
  34. {
  35. list.Add(new KBufferDescriptor(src, dst, size, state));
  36. return KernelResult.Success;
  37. }
  38. return KernelResult.OutOfMemory;
  39. }
  40. public KernelResult CopyBuffersToClient(KPageTableBase memoryManager)
  41. {
  42. KernelResult result = CopyToClient(memoryManager, _receiveBufferDescriptors);
  43. if (result != KernelResult.Success)
  44. {
  45. return result;
  46. }
  47. return CopyToClient(memoryManager, _exchangeBufferDescriptors);
  48. }
  49. private KernelResult CopyToClient(KPageTableBase memoryManager, List<KBufferDescriptor> list)
  50. {
  51. foreach (KBufferDescriptor desc in list)
  52. {
  53. MemoryState stateMask;
  54. switch (desc.State)
  55. {
  56. case MemoryState.IpcBuffer0: stateMask = MemoryState.IpcSendAllowedType0; break;
  57. case MemoryState.IpcBuffer1: stateMask = MemoryState.IpcSendAllowedType1; break;
  58. case MemoryState.IpcBuffer3: stateMask = MemoryState.IpcSendAllowedType3; break;
  59. default: return KernelResult.InvalidCombination;
  60. }
  61. MemoryAttribute attributeMask = MemoryAttribute.Borrowed | MemoryAttribute.Uncached;
  62. if (desc.State == MemoryState.IpcBuffer0)
  63. {
  64. attributeMask |= MemoryAttribute.DeviceMapped;
  65. }
  66. ulong clientAddrTruncated = BitUtils.AlignDown(desc.ClientAddress, KPageTableBase.PageSize);
  67. ulong clientAddrRounded = BitUtils.AlignUp (desc.ClientAddress, KPageTableBase.PageSize);
  68. // Check if address is not aligned, in this case we need to perform 2 copies.
  69. if (clientAddrTruncated != clientAddrRounded)
  70. {
  71. ulong copySize = clientAddrRounded - desc.ClientAddress;
  72. if (copySize > desc.Size)
  73. {
  74. copySize = desc.Size;
  75. }
  76. KernelResult result = memoryManager.CopyDataFromCurrentProcess(
  77. desc.ClientAddress,
  78. copySize,
  79. stateMask,
  80. stateMask,
  81. KMemoryPermission.ReadAndWrite,
  82. attributeMask,
  83. MemoryAttribute.None,
  84. desc.ServerAddress);
  85. if (result != KernelResult.Success)
  86. {
  87. return result;
  88. }
  89. }
  90. ulong clientEndAddr = desc.ClientAddress + desc.Size;
  91. ulong serverEndAddr = desc.ServerAddress + desc.Size;
  92. ulong clientEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KPageTableBase.PageSize);
  93. ulong clientEndAddrRounded = BitUtils.AlignUp (clientEndAddr, KPageTableBase.PageSize);
  94. ulong serverEndAddrTruncated = BitUtils.AlignDown(serverEndAddr, KPageTableBase.PageSize);
  95. if (clientEndAddrTruncated < clientEndAddrRounded &&
  96. (clientAddrTruncated == clientAddrRounded || clientAddrTruncated < clientEndAddrTruncated))
  97. {
  98. KernelResult result = memoryManager.CopyDataFromCurrentProcess(
  99. clientEndAddrTruncated,
  100. clientEndAddr - clientEndAddrTruncated,
  101. stateMask,
  102. stateMask,
  103. KMemoryPermission.ReadAndWrite,
  104. attributeMask,
  105. MemoryAttribute.None,
  106. serverEndAddrTruncated);
  107. if (result != KernelResult.Success)
  108. {
  109. return result;
  110. }
  111. }
  112. }
  113. return KernelResult.Success;
  114. }
  115. public KernelResult UnmapServerBuffers(KPageTableBase memoryManager)
  116. {
  117. KernelResult result = UnmapServer(memoryManager, _sendBufferDescriptors);
  118. if (result != KernelResult.Success)
  119. {
  120. return result;
  121. }
  122. result = UnmapServer(memoryManager, _receiveBufferDescriptors);
  123. if (result != KernelResult.Success)
  124. {
  125. return result;
  126. }
  127. return UnmapServer(memoryManager, _exchangeBufferDescriptors);
  128. }
  129. private KernelResult UnmapServer(KPageTableBase memoryManager, List<KBufferDescriptor> list)
  130. {
  131. foreach (KBufferDescriptor descriptor in list)
  132. {
  133. KernelResult result = memoryManager.UnmapNoAttributeIfStateEquals(
  134. descriptor.ServerAddress,
  135. descriptor.Size,
  136. descriptor.State);
  137. if (result != KernelResult.Success)
  138. {
  139. return result;
  140. }
  141. }
  142. return KernelResult.Success;
  143. }
  144. public KernelResult RestoreClientBuffers(KPageTableBase memoryManager)
  145. {
  146. KernelResult result = RestoreClient(memoryManager, _sendBufferDescriptors);
  147. if (result != KernelResult.Success)
  148. {
  149. return result;
  150. }
  151. result = RestoreClient(memoryManager, _receiveBufferDescriptors);
  152. if (result != KernelResult.Success)
  153. {
  154. return result;
  155. }
  156. return RestoreClient(memoryManager, _exchangeBufferDescriptors);
  157. }
  158. private KernelResult RestoreClient(KPageTableBase memoryManager, List<KBufferDescriptor> list)
  159. {
  160. foreach (KBufferDescriptor descriptor in list)
  161. {
  162. KernelResult result = memoryManager.UnmapIpcRestorePermission(
  163. descriptor.ClientAddress,
  164. descriptor.Size,
  165. descriptor.State);
  166. if (result != KernelResult.Success)
  167. {
  168. return result;
  169. }
  170. }
  171. return KernelResult.Success;
  172. }
  173. }
  174. }