KServerSession.cs 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248
  1. using Ryujinx.Common;
  2. using Ryujinx.HLE.HOS.Kernel.Common;
  3. using Ryujinx.HLE.HOS.Kernel.Memory;
  4. using Ryujinx.HLE.HOS.Kernel.Process;
  5. using Ryujinx.HLE.HOS.Kernel.Threading;
  6. using Ryujinx.Horizon.Common;
  7. using System.Collections.Generic;
  8. namespace Ryujinx.HLE.HOS.Kernel.Ipc
  9. {
  10. class KServerSession : KSynchronizationObject
  11. {
  12. private static readonly MemoryState[] IpcMemoryStates = new MemoryState[]
  13. {
  14. MemoryState.IpcBuffer3,
  15. MemoryState.IpcBuffer0,
  16. MemoryState.IpcBuffer1,
  17. (MemoryState)0xfffce5d4 //This is invalid, shouldn't be accessed.
  18. };
  19. private readonly struct Message
  20. {
  21. public ulong Address { get; }
  22. public ulong Size { get; }
  23. public bool IsCustom { get; }
  24. public Message(KThread thread, ulong customCmdBuffAddress, ulong customCmdBuffSize)
  25. {
  26. IsCustom = customCmdBuffAddress != 0;
  27. if (IsCustom)
  28. {
  29. Address = customCmdBuffAddress;
  30. Size = customCmdBuffSize;
  31. }
  32. else
  33. {
  34. Address = thread.TlsAddress;
  35. Size = 0x100;
  36. }
  37. }
  38. public Message(KSessionRequest request) : this(
  39. request.ClientThread,
  40. request.CustomCmdBuffAddr,
  41. request.CustomCmdBuffSize) { }
  42. }
  43. private readonly struct MessageHeader
  44. {
  45. public uint Word0 { get; }
  46. public uint Word1 { get; }
  47. public uint Word2 { get; }
  48. public uint PointerBuffersCount { get; }
  49. public uint SendBuffersCount { get; }
  50. public uint ReceiveBuffersCount { get; }
  51. public uint ExchangeBuffersCount { get; }
  52. public uint RawDataSizeInWords { get; }
  53. public uint ReceiveListType { get; }
  54. public uint MessageSizeInWords { get; }
  55. public uint ReceiveListOffsetInWords { get; }
  56. public uint ReceiveListOffset { get; }
  57. public bool HasHandles { get; }
  58. public bool HasPid { get; }
  59. public uint CopyHandlesCount { get; }
  60. public uint MoveHandlesCount { get; }
  61. public MessageHeader(uint word0, uint word1, uint word2)
  62. {
  63. Word0 = word0;
  64. Word1 = word1;
  65. Word2 = word2;
  66. HasHandles = word1 >> 31 != 0;
  67. uint handleDescSizeInWords = 0;
  68. if (HasHandles)
  69. {
  70. uint pidSize = (word2 & 1) * 8;
  71. HasPid = pidSize != 0;
  72. CopyHandlesCount = (word2 >> 1) & 0xf;
  73. MoveHandlesCount = (word2 >> 5) & 0xf;
  74. handleDescSizeInWords = (pidSize + CopyHandlesCount * 4 + MoveHandlesCount * 4) / 4;
  75. }
  76. else
  77. {
  78. HasPid = false;
  79. CopyHandlesCount = 0;
  80. MoveHandlesCount = 0;
  81. }
  82. PointerBuffersCount = (word0 >> 16) & 0xf;
  83. SendBuffersCount = (word0 >> 20) & 0xf;
  84. ReceiveBuffersCount = (word0 >> 24) & 0xf;
  85. ExchangeBuffersCount = word0 >> 28;
  86. uint pointerDescSizeInWords = PointerBuffersCount * 2;
  87. uint sendDescSizeInWords = SendBuffersCount * 3;
  88. uint receiveDescSizeInWords = ReceiveBuffersCount * 3;
  89. uint exchangeDescSizeInWords = ExchangeBuffersCount * 3;
  90. RawDataSizeInWords = word1 & 0x3ff;
  91. ReceiveListType = (word1 >> 10) & 0xf;
  92. ReceiveListOffsetInWords = (word1 >> 20) & 0x7ff;
  93. uint paddingSizeInWords = HasHandles ? 3u : 2u;
  94. MessageSizeInWords = pointerDescSizeInWords +
  95. sendDescSizeInWords +
  96. receiveDescSizeInWords +
  97. exchangeDescSizeInWords +
  98. RawDataSizeInWords +
  99. paddingSizeInWords +
  100. handleDescSizeInWords;
  101. if (ReceiveListOffsetInWords == 0)
  102. {
  103. ReceiveListOffsetInWords = MessageSizeInWords;
  104. }
  105. ReceiveListOffset = ReceiveListOffsetInWords * 4;
  106. }
  107. }
  108. private struct PointerBufferDesc
  109. {
  110. public uint ReceiveIndex { get; }
  111. public uint BufferSize { get; }
  112. public ulong BufferAddress { get; set; }
  113. public PointerBufferDesc(ulong dword)
  114. {
  115. ReceiveIndex = (uint)dword & 0xf;
  116. BufferSize = (uint)dword >> 16;
  117. BufferAddress = (dword >> 2) & 0x70;
  118. BufferAddress |= (dword >> 12) & 0xf;
  119. BufferAddress = (BufferAddress << 32) | (dword >> 32);
  120. }
  121. public ulong Pack()
  122. {
  123. ulong dword = (ReceiveIndex & 0xf) | ((BufferSize & 0xffff) << 16);
  124. dword |= BufferAddress << 32;
  125. dword |= (BufferAddress >> 20) & 0xf000;
  126. dword |= (BufferAddress >> 30) & 0xffc0;
  127. return dword;
  128. }
  129. }
  130. private KSession _parent;
  131. private LinkedList<KSessionRequest> _requests;
  132. private KSessionRequest _activeRequest;
  133. public KServerSession(KernelContext context, KSession parent) : base(context)
  134. {
  135. _parent = parent;
  136. _requests = new LinkedList<KSessionRequest>();
  137. }
  138. public Result EnqueueRequest(KSessionRequest request)
  139. {
  140. if (_parent.ClientSession.State != ChannelState.Open)
  141. {
  142. return KernelResult.PortRemoteClosed;
  143. }
  144. if (request.AsyncEvent == null)
  145. {
  146. if (request.ClientThread.ShallBeTerminated ||
  147. request.ClientThread.SchedFlags == ThreadSchedState.TerminationPending)
  148. {
  149. return KernelResult.ThreadTerminating;
  150. }
  151. request.ClientThread.Reschedule(ThreadSchedState.Paused);
  152. }
  153. _requests.AddLast(request);
  154. if (_requests.Count == 1)
  155. {
  156. Signal();
  157. }
  158. return Result.Success;
  159. }
  160. public Result Receive(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
  161. {
  162. KThread serverThread = KernelStatic.GetCurrentThread();
  163. KProcess serverProcess = serverThread.Owner;
  164. KernelContext.CriticalSection.Enter();
  165. if (_parent.ClientSession.State != ChannelState.Open)
  166. {
  167. KernelContext.CriticalSection.Leave();
  168. return KernelResult.PortRemoteClosed;
  169. }
  170. if (_activeRequest != null || !DequeueRequest(out KSessionRequest request))
  171. {
  172. KernelContext.CriticalSection.Leave();
  173. return KernelResult.NotFound;
  174. }
  175. if (request.ClientThread == null)
  176. {
  177. KernelContext.CriticalSection.Leave();
  178. return KernelResult.PortRemoteClosed;
  179. }
  180. KThread clientThread = request.ClientThread;
  181. KProcess clientProcess = clientThread.Owner;
  182. KernelContext.CriticalSection.Leave();
  183. _activeRequest = request;
  184. request.ServerProcess = serverProcess;
  185. Message clientMsg = new Message(request);
  186. Message serverMsg = new Message(serverThread, customCmdBuffAddr, customCmdBuffSize);
  187. MessageHeader clientHeader = GetClientMessageHeader(clientProcess, clientMsg);
  188. MessageHeader serverHeader = GetServerMessageHeader(serverMsg);
  189. Result serverResult = KernelResult.NotFound;
  190. Result clientResult = Result.Success;
  191. void CleanUpForError()
  192. {
  193. if (request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager) == Result.Success)
  194. {
  195. request.BufferDescriptorTable.RestoreClientBuffers(clientProcess.MemoryManager);
  196. }
  197. CloseAllHandles(serverMsg, clientHeader, serverProcess);
  198. KernelContext.CriticalSection.Enter();
  199. _activeRequest = null;
  200. if (_requests.Count != 0)
  201. {
  202. Signal();
  203. }
  204. KernelContext.CriticalSection.Leave();
  205. WakeClientThread(request, clientResult);
  206. }
  207. if (clientHeader.ReceiveListType < 2 &&
  208. clientHeader.ReceiveListOffset > clientMsg.Size)
  209. {
  210. CleanUpForError();
  211. return KernelResult.InvalidCombination;
  212. }
  213. else if (clientHeader.ReceiveListType == 2 &&
  214. clientHeader.ReceiveListOffset + 8 > clientMsg.Size)
  215. {
  216. CleanUpForError();
  217. return KernelResult.InvalidCombination;
  218. }
  219. else if (clientHeader.ReceiveListType > 2 &&
  220. clientHeader.ReceiveListType * 8 - 0x10 + clientHeader.ReceiveListOffset > clientMsg.Size)
  221. {
  222. CleanUpForError();
  223. return KernelResult.InvalidCombination;
  224. }
  225. if (clientHeader.ReceiveListOffsetInWords < clientHeader.MessageSizeInWords)
  226. {
  227. CleanUpForError();
  228. return KernelResult.InvalidCombination;
  229. }
  230. if (clientHeader.MessageSizeInWords * 4 > clientMsg.Size)
  231. {
  232. CleanUpForError();
  233. return KernelResult.CmdBufferTooSmall;
  234. }
  235. ulong[] receiveList = GetReceiveList(
  236. serverProcess,
  237. serverMsg,
  238. serverHeader.ReceiveListType,
  239. serverHeader.ReceiveListOffset);
  240. serverProcess.CpuMemory.Write(serverMsg.Address + 0, clientHeader.Word0);
  241. serverProcess.CpuMemory.Write(serverMsg.Address + 4, clientHeader.Word1);
  242. uint offset;
  243. // Copy handles.
  244. if (clientHeader.HasHandles)
  245. {
  246. if (clientHeader.MoveHandlesCount != 0)
  247. {
  248. CleanUpForError();
  249. return KernelResult.InvalidCombination;
  250. }
  251. serverProcess.CpuMemory.Write(serverMsg.Address + 8, clientHeader.Word2);
  252. offset = 3;
  253. if (clientHeader.HasPid)
  254. {
  255. serverProcess.CpuMemory.Write(serverMsg.Address + offset * 4, clientProcess.Pid);
  256. offset += 2;
  257. }
  258. for (int index = 0; index < clientHeader.CopyHandlesCount; index++)
  259. {
  260. int newHandle = 0;
  261. int handle = clientProcess.CpuMemory.Read<int>(clientMsg.Address + offset * 4);
  262. if (clientResult == Result.Success && handle != 0)
  263. {
  264. clientResult = GetCopyObjectHandle(clientThread, serverProcess, handle, out newHandle);
  265. }
  266. serverProcess.CpuMemory.Write(serverMsg.Address + offset * 4, newHandle);
  267. offset++;
  268. }
  269. for (int index = 0; index < clientHeader.MoveHandlesCount; index++)
  270. {
  271. int newHandle = 0;
  272. int handle = clientProcess.CpuMemory.Read<int>(clientMsg.Address + offset * 4);
  273. if (handle != 0)
  274. {
  275. if (clientResult == Result.Success)
  276. {
  277. clientResult = GetMoveObjectHandle(clientProcess, serverProcess, handle, out newHandle);
  278. }
  279. else
  280. {
  281. clientProcess.HandleTable.CloseHandle(handle);
  282. }
  283. }
  284. serverProcess.CpuMemory.Write(serverMsg.Address + offset * 4, newHandle);
  285. offset++;
  286. }
  287. if (clientResult != Result.Success)
  288. {
  289. CleanUpForError();
  290. return serverResult;
  291. }
  292. }
  293. else
  294. {
  295. offset = 2;
  296. }
  297. // Copy pointer/receive list buffers.
  298. uint recvListDstOffset = 0;
  299. for (int index = 0; index < clientHeader.PointerBuffersCount; index++)
  300. {
  301. ulong pointerDesc = clientProcess.CpuMemory.Read<ulong>(clientMsg.Address + offset * 4);
  302. PointerBufferDesc descriptor = new PointerBufferDesc(pointerDesc);
  303. if (descriptor.BufferSize != 0)
  304. {
  305. clientResult = GetReceiveListAddress(
  306. descriptor,
  307. serverMsg,
  308. serverHeader.ReceiveListType,
  309. clientHeader.MessageSizeInWords,
  310. receiveList,
  311. ref recvListDstOffset,
  312. out ulong recvListBufferAddress);
  313. if (clientResult != Result.Success)
  314. {
  315. CleanUpForError();
  316. return serverResult;
  317. }
  318. clientResult = clientProcess.MemoryManager.CopyDataToCurrentProcess(
  319. recvListBufferAddress,
  320. descriptor.BufferSize,
  321. descriptor.BufferAddress,
  322. MemoryState.IsPoolAllocated,
  323. MemoryState.IsPoolAllocated,
  324. KMemoryPermission.Read,
  325. MemoryAttribute.Uncached,
  326. MemoryAttribute.None);
  327. if (clientResult != Result.Success)
  328. {
  329. CleanUpForError();
  330. return serverResult;
  331. }
  332. descriptor.BufferAddress = recvListBufferAddress;
  333. }
  334. else
  335. {
  336. descriptor.BufferAddress = 0;
  337. }
  338. serverProcess.CpuMemory.Write(serverMsg.Address + offset * 4, descriptor.Pack());
  339. offset += 2;
  340. }
  341. // Copy send, receive and exchange buffers.
  342. uint totalBuffersCount =
  343. clientHeader.SendBuffersCount +
  344. clientHeader.ReceiveBuffersCount +
  345. clientHeader.ExchangeBuffersCount;
  346. for (int index = 0; index < totalBuffersCount; index++)
  347. {
  348. ulong clientDescAddress = clientMsg.Address + offset * 4;
  349. uint descWord0 = clientProcess.CpuMemory.Read<uint>(clientDescAddress + 0);
  350. uint descWord1 = clientProcess.CpuMemory.Read<uint>(clientDescAddress + 4);
  351. uint descWord2 = clientProcess.CpuMemory.Read<uint>(clientDescAddress + 8);
  352. bool isSendDesc = index < clientHeader.SendBuffersCount;
  353. bool isExchangeDesc = index >= clientHeader.SendBuffersCount + clientHeader.ReceiveBuffersCount;
  354. bool notReceiveDesc = isSendDesc || isExchangeDesc;
  355. bool isReceiveDesc = !notReceiveDesc;
  356. KMemoryPermission permission = index >= clientHeader.SendBuffersCount
  357. ? KMemoryPermission.ReadAndWrite
  358. : KMemoryPermission.Read;
  359. uint sizeHigh4 = (descWord2 >> 24) & 0xf;
  360. ulong bufferSize = descWord0 | (ulong)sizeHigh4 << 32;
  361. ulong dstAddress = 0;
  362. if (bufferSize != 0)
  363. {
  364. ulong bufferAddress;
  365. bufferAddress = descWord2 >> 28;
  366. bufferAddress |= ((descWord2 >> 2) & 7) << 4;
  367. bufferAddress = (bufferAddress << 32) | descWord1;
  368. MemoryState state = IpcMemoryStates[(descWord2 + 1) & 3];
  369. clientResult = serverProcess.MemoryManager.MapBufferFromClientProcess(
  370. bufferSize,
  371. bufferAddress,
  372. clientProcess.MemoryManager,
  373. permission,
  374. state,
  375. notReceiveDesc,
  376. out dstAddress);
  377. if (clientResult != Result.Success)
  378. {
  379. CleanUpForError();
  380. return serverResult;
  381. }
  382. if (isSendDesc)
  383. {
  384. clientResult = request.BufferDescriptorTable.AddSendBuffer(bufferAddress, dstAddress, bufferSize, state);
  385. }
  386. else if (isReceiveDesc)
  387. {
  388. clientResult = request.BufferDescriptorTable.AddReceiveBuffer(bufferAddress, dstAddress, bufferSize, state);
  389. }
  390. else /* if (isExchangeDesc) */
  391. {
  392. clientResult = request.BufferDescriptorTable.AddExchangeBuffer(bufferAddress, dstAddress, bufferSize, state);
  393. }
  394. if (clientResult != Result.Success)
  395. {
  396. CleanUpForError();
  397. return serverResult;
  398. }
  399. }
  400. descWord1 = (uint)dstAddress;
  401. descWord2 &= 3;
  402. descWord2 |= sizeHigh4 << 24;
  403. descWord2 |= (uint)(dstAddress >> 34) & 0x3ffffffc;
  404. descWord2 |= (uint)(dstAddress >> 4) & 0xf0000000;
  405. ulong serverDescAddress = serverMsg.Address + offset * 4;
  406. serverProcess.CpuMemory.Write(serverDescAddress + 0, descWord0);
  407. serverProcess.CpuMemory.Write(serverDescAddress + 4, descWord1);
  408. serverProcess.CpuMemory.Write(serverDescAddress + 8, descWord2);
  409. offset += 3;
  410. }
  411. // Copy raw data.
  412. if (clientHeader.RawDataSizeInWords != 0)
  413. {
  414. ulong copySrc = clientMsg.Address + offset * 4;
  415. ulong copyDst = serverMsg.Address + offset * 4;
  416. ulong copySize = clientHeader.RawDataSizeInWords * 4;
  417. if (serverMsg.IsCustom || clientMsg.IsCustom)
  418. {
  419. KMemoryPermission permission = clientMsg.IsCustom
  420. ? KMemoryPermission.None
  421. : KMemoryPermission.Read;
  422. clientResult = clientProcess.MemoryManager.CopyDataToCurrentProcess(
  423. copyDst,
  424. copySize,
  425. copySrc,
  426. MemoryState.IsPoolAllocated,
  427. MemoryState.IsPoolAllocated,
  428. permission,
  429. MemoryAttribute.Uncached,
  430. MemoryAttribute.None);
  431. }
  432. else
  433. {
  434. serverProcess.CpuMemory.Write(copyDst, clientProcess.CpuMemory.GetSpan(copySrc, (int)copySize));
  435. }
  436. if (clientResult != Result.Success)
  437. {
  438. CleanUpForError();
  439. return serverResult;
  440. }
  441. }
  442. return Result.Success;
  443. }
  444. public Result Reply(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
  445. {
  446. KThread serverThread = KernelStatic.GetCurrentThread();
  447. KProcess serverProcess = serverThread.Owner;
  448. KernelContext.CriticalSection.Enter();
  449. if (_activeRequest == null)
  450. {
  451. KernelContext.CriticalSection.Leave();
  452. return KernelResult.InvalidState;
  453. }
  454. KSessionRequest request = _activeRequest;
  455. _activeRequest = null;
  456. if (_requests.Count != 0)
  457. {
  458. Signal();
  459. }
  460. KernelContext.CriticalSection.Leave();
  461. KThread clientThread = request.ClientThread;
  462. KProcess clientProcess = clientThread.Owner;
  463. Message clientMsg = new Message(request);
  464. Message serverMsg = new Message(serverThread, customCmdBuffAddr, customCmdBuffSize);
  465. MessageHeader clientHeader = GetClientMessageHeader(clientProcess, clientMsg);
  466. MessageHeader serverHeader = GetServerMessageHeader(serverMsg);
  467. Result clientResult = Result.Success;
  468. Result serverResult = Result.Success;
  469. void CleanUpForError()
  470. {
  471. CloseAllHandles(clientMsg, serverHeader, clientProcess);
  472. FinishRequest(request, clientResult);
  473. }
  474. if (clientHeader.ReceiveListType < 2 &&
  475. clientHeader.ReceiveListOffset > clientMsg.Size)
  476. {
  477. CleanUpForError();
  478. return KernelResult.InvalidCombination;
  479. }
  480. else if (clientHeader.ReceiveListType == 2 &&
  481. clientHeader.ReceiveListOffset + 8 > clientMsg.Size)
  482. {
  483. CleanUpForError();
  484. return KernelResult.InvalidCombination;
  485. }
  486. else if (clientHeader.ReceiveListType > 2 &&
  487. clientHeader.ReceiveListType * 8 - 0x10 + clientHeader.ReceiveListOffset > clientMsg.Size)
  488. {
  489. CleanUpForError();
  490. return KernelResult.InvalidCombination;
  491. }
  492. if (clientHeader.ReceiveListOffsetInWords < clientHeader.MessageSizeInWords)
  493. {
  494. CleanUpForError();
  495. return KernelResult.InvalidCombination;
  496. }
  497. if (serverHeader.MessageSizeInWords * 4 > clientMsg.Size)
  498. {
  499. CleanUpForError();
  500. return KernelResult.CmdBufferTooSmall;
  501. }
  502. if (serverHeader.SendBuffersCount != 0 ||
  503. serverHeader.ReceiveBuffersCount != 0 ||
  504. serverHeader.ExchangeBuffersCount != 0)
  505. {
  506. CleanUpForError();
  507. return KernelResult.InvalidCombination;
  508. }
  509. // Read receive list.
  510. ulong[] receiveList = GetReceiveList(
  511. clientProcess,
  512. clientMsg,
  513. clientHeader.ReceiveListType,
  514. clientHeader.ReceiveListOffset);
  515. // Copy receive and exchange buffers.
  516. clientResult = request.BufferDescriptorTable.CopyBuffersToClient(clientProcess.MemoryManager);
  517. if (clientResult != Result.Success)
  518. {
  519. CleanUpForError();
  520. return serverResult;
  521. }
  522. // Copy header.
  523. clientProcess.CpuMemory.Write(clientMsg.Address + 0, serverHeader.Word0);
  524. clientProcess.CpuMemory.Write(clientMsg.Address + 4, serverHeader.Word1);
  525. // Copy handles.
  526. uint offset;
  527. if (serverHeader.HasHandles)
  528. {
  529. offset = 3;
  530. clientProcess.CpuMemory.Write(clientMsg.Address + 8, serverHeader.Word2);
  531. if (serverHeader.HasPid)
  532. {
  533. clientProcess.CpuMemory.Write(clientMsg.Address + offset * 4, serverProcess.Pid);
  534. offset += 2;
  535. }
  536. for (int index = 0; index < serverHeader.CopyHandlesCount; index++)
  537. {
  538. int newHandle = 0;
  539. int handle = serverProcess.CpuMemory.Read<int>(serverMsg.Address + offset * 4);
  540. if (handle != 0)
  541. {
  542. GetCopyObjectHandle(serverThread, clientProcess, handle, out newHandle);
  543. }
  544. clientProcess.CpuMemory.Write(clientMsg.Address + offset * 4, newHandle);
  545. offset++;
  546. }
  547. for (int index = 0; index < serverHeader.MoveHandlesCount; index++)
  548. {
  549. int newHandle = 0;
  550. int handle = serverProcess.CpuMemory.Read<int>(serverMsg.Address + offset * 4);
  551. if (handle != 0)
  552. {
  553. if (clientResult == Result.Success)
  554. {
  555. clientResult = GetMoveObjectHandle(serverProcess, clientProcess, handle, out newHandle);
  556. }
  557. else
  558. {
  559. serverProcess.HandleTable.CloseHandle(handle);
  560. }
  561. }
  562. clientProcess.CpuMemory.Write(clientMsg.Address + offset * 4, newHandle);
  563. offset++;
  564. }
  565. }
  566. else
  567. {
  568. offset = 2;
  569. }
  570. // Copy pointer/receive list buffers.
  571. uint recvListDstOffset = 0;
  572. for (int index = 0; index < serverHeader.PointerBuffersCount; index++)
  573. {
  574. ulong pointerDesc = serverProcess.CpuMemory.Read<ulong>(serverMsg.Address + offset * 4);
  575. PointerBufferDesc descriptor = new PointerBufferDesc(pointerDesc);
  576. ulong recvListBufferAddress = 0;
  577. if (descriptor.BufferSize != 0)
  578. {
  579. clientResult = GetReceiveListAddress(
  580. descriptor,
  581. clientMsg,
  582. clientHeader.ReceiveListType,
  583. serverHeader.MessageSizeInWords,
  584. receiveList,
  585. ref recvListDstOffset,
  586. out recvListBufferAddress);
  587. if (clientResult != Result.Success)
  588. {
  589. CleanUpForError();
  590. return serverResult;
  591. }
  592. clientResult = clientProcess.MemoryManager.CopyDataFromCurrentProcess(
  593. recvListBufferAddress,
  594. descriptor.BufferSize,
  595. MemoryState.IsPoolAllocated,
  596. MemoryState.IsPoolAllocated,
  597. KMemoryPermission.Read,
  598. MemoryAttribute.Uncached,
  599. MemoryAttribute.None,
  600. descriptor.BufferAddress);
  601. if (clientResult != Result.Success)
  602. {
  603. CleanUpForError();
  604. return serverResult;
  605. }
  606. }
  607. ulong dstDescAddress = clientMsg.Address + offset * 4;
  608. ulong clientPointerDesc =
  609. (recvListBufferAddress << 32) |
  610. ((recvListBufferAddress >> 20) & 0xf000) |
  611. ((recvListBufferAddress >> 30) & 0xffc0);
  612. clientPointerDesc |= pointerDesc & 0xffff000f;
  613. clientProcess.CpuMemory.Write(dstDescAddress + 0, clientPointerDesc);
  614. offset += 2;
  615. }
  616. // Set send, receive and exchange buffer descriptors to zero.
  617. uint totalBuffersCount =
  618. serverHeader.SendBuffersCount +
  619. serverHeader.ReceiveBuffersCount +
  620. serverHeader.ExchangeBuffersCount;
  621. for (int index = 0; index < totalBuffersCount; index++)
  622. {
  623. ulong dstDescAddress = clientMsg.Address + offset * 4;
  624. clientProcess.CpuMemory.Write(dstDescAddress + 0, 0);
  625. clientProcess.CpuMemory.Write(dstDescAddress + 4, 0);
  626. clientProcess.CpuMemory.Write(dstDescAddress + 8, 0);
  627. offset += 3;
  628. }
  629. // Copy raw data.
  630. if (serverHeader.RawDataSizeInWords != 0)
  631. {
  632. ulong copyDst = clientMsg.Address + offset * 4;
  633. ulong copySrc = serverMsg.Address + offset * 4;
  634. ulong copySize = serverHeader.RawDataSizeInWords * 4;
  635. if (serverMsg.IsCustom || clientMsg.IsCustom)
  636. {
  637. KMemoryPermission permission = clientMsg.IsCustom
  638. ? KMemoryPermission.None
  639. : KMemoryPermission.Read;
  640. clientResult = clientProcess.MemoryManager.CopyDataFromCurrentProcess(
  641. copyDst,
  642. copySize,
  643. MemoryState.IsPoolAllocated,
  644. MemoryState.IsPoolAllocated,
  645. permission,
  646. MemoryAttribute.Uncached,
  647. MemoryAttribute.None,
  648. copySrc);
  649. }
  650. else
  651. {
  652. clientProcess.CpuMemory.Write(copyDst, serverProcess.CpuMemory.GetSpan(copySrc, (int)copySize));
  653. }
  654. }
  655. // Unmap buffers from server.
  656. FinishRequest(request, clientResult);
  657. return serverResult;
  658. }
  659. private MessageHeader GetClientMessageHeader(KProcess clientProcess, Message clientMsg)
  660. {
  661. uint word0 = clientProcess.CpuMemory.Read<uint>(clientMsg.Address + 0);
  662. uint word1 = clientProcess.CpuMemory.Read<uint>(clientMsg.Address + 4);
  663. uint word2 = clientProcess.CpuMemory.Read<uint>(clientMsg.Address + 8);
  664. return new MessageHeader(word0, word1, word2);
  665. }
  666. private MessageHeader GetServerMessageHeader(Message serverMsg)
  667. {
  668. KProcess currentProcess = KernelStatic.GetCurrentProcess();
  669. uint word0 = currentProcess.CpuMemory.Read<uint>(serverMsg.Address + 0);
  670. uint word1 = currentProcess.CpuMemory.Read<uint>(serverMsg.Address + 4);
  671. uint word2 = currentProcess.CpuMemory.Read<uint>(serverMsg.Address + 8);
  672. return new MessageHeader(word0, word1, word2);
  673. }
  674. private Result GetCopyObjectHandle(KThread srcThread, KProcess dstProcess, int srcHandle, out int dstHandle)
  675. {
  676. dstHandle = 0;
  677. KProcess srcProcess = srcThread.Owner;
  678. KAutoObject obj;
  679. if (srcHandle == KHandleTable.SelfProcessHandle)
  680. {
  681. obj = srcProcess;
  682. }
  683. else if (srcHandle == KHandleTable.SelfThreadHandle)
  684. {
  685. obj = srcThread;
  686. }
  687. else
  688. {
  689. obj = srcProcess.HandleTable.GetObject<KAutoObject>(srcHandle);
  690. }
  691. if (obj != null)
  692. {
  693. return dstProcess.HandleTable.GenerateHandle(obj, out dstHandle);
  694. }
  695. else
  696. {
  697. return KernelResult.InvalidHandle;
  698. }
  699. }
  700. private Result GetMoveObjectHandle(KProcess srcProcess, KProcess dstProcess, int srcHandle, out int dstHandle)
  701. {
  702. dstHandle = 0;
  703. KAutoObject obj = srcProcess.HandleTable.GetObject<KAutoObject>(srcHandle);
  704. if (obj != null)
  705. {
  706. Result result = dstProcess.HandleTable.GenerateHandle(obj, out dstHandle);
  707. srcProcess.HandleTable.CloseHandle(srcHandle);
  708. return result;
  709. }
  710. else
  711. {
  712. return KernelResult.InvalidHandle;
  713. }
  714. }
  715. private ulong[] GetReceiveList(KProcess ownerProcess, Message message, uint recvListType, uint recvListOffset)
  716. {
  717. int recvListSize = 0;
  718. if (recvListType >= 3)
  719. {
  720. recvListSize = (int)recvListType - 2;
  721. }
  722. else if (recvListType == 2)
  723. {
  724. recvListSize = 1;
  725. }
  726. ulong[] receiveList = new ulong[recvListSize];
  727. ulong recvListAddress = message.Address + recvListOffset;
  728. for (int index = 0; index < recvListSize; index++)
  729. {
  730. receiveList[index] = ownerProcess.CpuMemory.Read<ulong>(recvListAddress + (ulong)index * 8);
  731. }
  732. return receiveList;
  733. }
  734. private Result GetReceiveListAddress(
  735. PointerBufferDesc descriptor,
  736. Message message,
  737. uint recvListType,
  738. uint messageSizeInWords,
  739. ulong[] receiveList,
  740. ref uint dstOffset,
  741. out ulong address)
  742. {
  743. ulong recvListBufferAddress = address = 0;
  744. if (recvListType == 0)
  745. {
  746. return KernelResult.OutOfResource;
  747. }
  748. else if (recvListType == 1 || recvListType == 2)
  749. {
  750. ulong recvListBaseAddr;
  751. ulong recvListEndAddr;
  752. if (recvListType == 1)
  753. {
  754. recvListBaseAddr = message.Address + messageSizeInWords * 4;
  755. recvListEndAddr = message.Address + message.Size;
  756. }
  757. else /* if (recvListType == 2) */
  758. {
  759. ulong packed = receiveList[0];
  760. recvListBaseAddr = packed & 0x7fffffffff;
  761. uint size = (uint)(packed >> 48);
  762. if (size == 0)
  763. {
  764. return KernelResult.OutOfResource;
  765. }
  766. recvListEndAddr = recvListBaseAddr + size;
  767. }
  768. recvListBufferAddress = BitUtils.AlignUp<ulong>(recvListBaseAddr + dstOffset, 0x10);
  769. ulong endAddress = recvListBufferAddress + descriptor.BufferSize;
  770. dstOffset = (uint)endAddress - (uint)recvListBaseAddr;
  771. if (recvListBufferAddress + descriptor.BufferSize <= recvListBufferAddress ||
  772. recvListBufferAddress + descriptor.BufferSize > recvListEndAddr)
  773. {
  774. return KernelResult.OutOfResource;
  775. }
  776. }
  777. else /* if (recvListType > 2) */
  778. {
  779. if (descriptor.ReceiveIndex >= receiveList.Length)
  780. {
  781. return KernelResult.OutOfResource;
  782. }
  783. ulong packed = receiveList[descriptor.ReceiveIndex];
  784. recvListBufferAddress = packed & 0x7fffffffff;
  785. uint size = (uint)(packed >> 48);
  786. if (recvListBufferAddress == 0 || size == 0 || size < descriptor.BufferSize)
  787. {
  788. return KernelResult.OutOfResource;
  789. }
  790. }
  791. address = recvListBufferAddress;
  792. return Result.Success;
  793. }
  794. private void CloseAllHandles(Message message, MessageHeader header, KProcess process)
  795. {
  796. if (header.HasHandles)
  797. {
  798. uint totalHandeslCount = header.CopyHandlesCount + header.MoveHandlesCount;
  799. uint offset = 3;
  800. if (header.HasPid)
  801. {
  802. process.CpuMemory.Write(message.Address + offset * 4, 0L);
  803. offset += 2;
  804. }
  805. for (int index = 0; index < totalHandeslCount; index++)
  806. {
  807. int handle = process.CpuMemory.Read<int>(message.Address + offset * 4);
  808. if (handle != 0)
  809. {
  810. process.HandleTable.CloseHandle(handle);
  811. process.CpuMemory.Write(message.Address + offset * 4, 0);
  812. }
  813. offset++;
  814. }
  815. }
  816. }
  817. public override bool IsSignaled()
  818. {
  819. if (_parent.ClientSession.State != ChannelState.Open)
  820. {
  821. return true;
  822. }
  823. return _requests.Count != 0 && _activeRequest == null;
  824. }
  825. protected override void Destroy()
  826. {
  827. _parent.DisconnectServer();
  828. CancelAllRequestsServerDisconnected();
  829. _parent.DecrementReferenceCount();
  830. }
  831. private void CancelAllRequestsServerDisconnected()
  832. {
  833. foreach (KSessionRequest request in IterateWithRemovalOfAllRequests())
  834. {
  835. FinishRequest(request, KernelResult.PortRemoteClosed);
  836. }
  837. }
  838. public void CancelAllRequestsClientDisconnected()
  839. {
  840. foreach (KSessionRequest request in IterateWithRemovalOfAllRequests())
  841. {
  842. if (request.ClientThread.ShallBeTerminated ||
  843. request.ClientThread.SchedFlags == ThreadSchedState.TerminationPending)
  844. {
  845. continue;
  846. }
  847. // Client sessions can only be disconnected on async requests (because
  848. // the client would be otherwise blocked waiting for the response), so
  849. // we only need to handle the async case here.
  850. if (request.AsyncEvent != null)
  851. {
  852. SendResultToAsyncRequestClient(request, KernelResult.PortRemoteClosed);
  853. }
  854. }
  855. WakeServerThreads(KernelResult.PortRemoteClosed);
  856. }
  857. private IEnumerable<KSessionRequest> IterateWithRemovalOfAllRequests()
  858. {
  859. KernelContext.CriticalSection.Enter();
  860. if (_activeRequest != null)
  861. {
  862. KSessionRequest request = _activeRequest;
  863. _activeRequest = null;
  864. KernelContext.CriticalSection.Leave();
  865. yield return request;
  866. }
  867. else
  868. {
  869. KernelContext.CriticalSection.Leave();
  870. }
  871. while (DequeueRequest(out KSessionRequest request))
  872. {
  873. yield return request;
  874. }
  875. }
  876. private bool DequeueRequest(out KSessionRequest request)
  877. {
  878. request = null;
  879. KernelContext.CriticalSection.Enter();
  880. bool hasRequest = _requests.First != null;
  881. if (hasRequest)
  882. {
  883. request = _requests.First.Value;
  884. _requests.RemoveFirst();
  885. }
  886. KernelContext.CriticalSection.Leave();
  887. return hasRequest;
  888. }
  889. private void FinishRequest(KSessionRequest request, Result result)
  890. {
  891. KProcess clientProcess = request.ClientThread.Owner;
  892. KProcess serverProcess = request.ServerProcess;
  893. Result unmapResult = Result.Success;
  894. if (serverProcess != null)
  895. {
  896. unmapResult = request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager);
  897. }
  898. if (unmapResult == Result.Success)
  899. {
  900. request.BufferDescriptorTable.RestoreClientBuffers(clientProcess.MemoryManager);
  901. }
  902. WakeClientThread(request, result);
  903. }
  904. private void WakeClientThread(KSessionRequest request, Result result)
  905. {
  906. // Wait client thread waiting for a response for the given request.
  907. if (request.AsyncEvent != null)
  908. {
  909. SendResultToAsyncRequestClient(request, result);
  910. }
  911. else
  912. {
  913. KernelContext.CriticalSection.Enter();
  914. WakeAndSetResult(request.ClientThread, result);
  915. KernelContext.CriticalSection.Leave();
  916. }
  917. }
  918. private void SendResultToAsyncRequestClient(KSessionRequest request, Result result)
  919. {
  920. KProcess clientProcess = request.ClientThread.Owner;
  921. if (result != Result.Success)
  922. {
  923. ulong address = request.CustomCmdBuffAddr;
  924. clientProcess.CpuMemory.Write<ulong>(address, 0);
  925. clientProcess.CpuMemory.Write(address + 8, result.ErrorCode);
  926. }
  927. clientProcess.MemoryManager.UnborrowIpcBuffer(request.CustomCmdBuffAddr, request.CustomCmdBuffSize);
  928. request.AsyncEvent.Signal();
  929. }
  930. private void WakeServerThreads(Result result)
  931. {
  932. // Wake all server threads waiting for requests.
  933. KernelContext.CriticalSection.Enter();
  934. foreach (KThread thread in WaitingThreads)
  935. {
  936. WakeAndSetResult(thread, result, this);
  937. }
  938. KernelContext.CriticalSection.Leave();
  939. }
  940. private void WakeAndSetResult(KThread thread, Result result, KSynchronizationObject signaledObj = null)
  941. {
  942. if ((thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
  943. {
  944. thread.SignaledObj = signaledObj;
  945. thread.ObjSyncResult = result;
  946. thread.Reschedule(ThreadSchedState.Running);
  947. }
  948. }
  949. }
  950. }