IpcService.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.HLE.Exceptions;
  3. using Ryujinx.HLE.HOS.Ipc;
  4. using Ryujinx.HLE.HOS.Kernel.Common;
  5. using Ryujinx.HLE.HOS.Kernel.Ipc;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.IO;
  9. using Ryujinx.Profiler;
  10. namespace Ryujinx.HLE.HOS.Services
  11. {
  12. abstract class IpcService : IIpcService
  13. {
  14. public abstract IReadOnlyDictionary<int, ServiceProcessRequest> Commands { get; }
  15. private IdDictionary _domainObjects;
  16. private int _selfId;
  17. private bool _isDomain;
  18. public IpcService()
  19. {
  20. _domainObjects = new IdDictionary();
  21. _selfId = -1;
  22. }
  23. public int ConvertToDomain()
  24. {
  25. if (_selfId == -1)
  26. {
  27. _selfId = _domainObjects.Add(this);
  28. }
  29. _isDomain = true;
  30. return _selfId;
  31. }
  32. public void ConvertToSession()
  33. {
  34. _isDomain = false;
  35. }
  36. public void CallMethod(ServiceCtx context)
  37. {
  38. IIpcService service = this;
  39. if (_isDomain)
  40. {
  41. int domainWord0 = context.RequestData.ReadInt32();
  42. int domainObjId = context.RequestData.ReadInt32();
  43. int domainCmd = (domainWord0 >> 0) & 0xff;
  44. int inputObjCount = (domainWord0 >> 8) & 0xff;
  45. int dataPayloadSize = (domainWord0 >> 16) & 0xffff;
  46. context.RequestData.BaseStream.Seek(0x10 + dataPayloadSize, SeekOrigin.Begin);
  47. for (int index = 0; index < inputObjCount; index++)
  48. {
  49. context.Request.ObjectIds.Add(context.RequestData.ReadInt32());
  50. }
  51. context.RequestData.BaseStream.Seek(0x10, SeekOrigin.Begin);
  52. if (domainCmd == 1)
  53. {
  54. service = GetObject(domainObjId);
  55. context.ResponseData.Write(0L);
  56. context.ResponseData.Write(0L);
  57. }
  58. else if (domainCmd == 2)
  59. {
  60. Delete(domainObjId);
  61. context.ResponseData.Write(0L);
  62. return;
  63. }
  64. else
  65. {
  66. throw new NotImplementedException($"Domain command: {domainCmd}");
  67. }
  68. }
  69. long sfciMagic = context.RequestData.ReadInt64();
  70. int commandId = (int)context.RequestData.ReadInt64();
  71. bool serviceExists = service.Commands.TryGetValue(commandId, out ServiceProcessRequest processRequest);
  72. if (ServiceConfiguration.IgnoreMissingServices || serviceExists)
  73. {
  74. long result = 0;
  75. context.ResponseData.BaseStream.Seek(_isDomain ? 0x20 : 0x10, SeekOrigin.Begin);
  76. if (serviceExists)
  77. {
  78. Logger.PrintDebug(LogClass.KernelIpc, $"{service.GetType().Name}: {processRequest.Method.Name}");
  79. ProfileConfig profile = Profiles.ServiceCall;
  80. profile.SessionGroup = service.GetType().Name;
  81. profile.SessionItem = processRequest.Method.Name;
  82. Profile.Begin(profile);
  83. result = processRequest(context);
  84. Profile.End(profile);
  85. }
  86. else
  87. {
  88. string serviceName;
  89. DummyService dummyService = service as DummyService;
  90. serviceName = (dummyService == null) ? service.GetType().FullName : dummyService.ServiceName;
  91. Logger.PrintWarning(LogClass.KernelIpc, $"Missing service {serviceName}: {commandId} ignored");
  92. }
  93. if (_isDomain)
  94. {
  95. foreach (int id in context.Response.ObjectIds)
  96. {
  97. context.ResponseData.Write(id);
  98. }
  99. context.ResponseData.BaseStream.Seek(0, SeekOrigin.Begin);
  100. context.ResponseData.Write(context.Response.ObjectIds.Count);
  101. }
  102. context.ResponseData.BaseStream.Seek(_isDomain ? 0x10 : 0, SeekOrigin.Begin);
  103. context.ResponseData.Write(IpcMagic.Sfco);
  104. context.ResponseData.Write(result);
  105. }
  106. else
  107. {
  108. string dbgMessage = $"{service.GetType().FullName}: {commandId}";
  109. throw new ServiceNotImplementedException(context, dbgMessage);
  110. }
  111. }
  112. protected static void MakeObject(ServiceCtx context, IpcService obj)
  113. {
  114. IpcService service = context.Session.Service;
  115. if (service._isDomain)
  116. {
  117. context.Response.ObjectIds.Add(service.Add(obj));
  118. }
  119. else
  120. {
  121. KSession session = new KSession(context.Device.System);
  122. session.ClientSession.Service = obj;
  123. if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
  124. {
  125. throw new InvalidOperationException("Out of handles!");
  126. }
  127. context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
  128. }
  129. }
  130. protected static T GetObject<T>(ServiceCtx context, int index) where T : IpcService
  131. {
  132. IpcService service = context.Session.Service;
  133. if (!service._isDomain)
  134. {
  135. int handle = context.Request.HandleDesc.ToMove[index];
  136. KClientSession session = context.Process.HandleTable.GetObject<KClientSession>(handle);
  137. return session?.Service is T ? (T)session.Service : null;
  138. }
  139. int objId = context.Request.ObjectIds[index];
  140. IIpcService obj = service.GetObject(objId);
  141. return obj is T ? (T)obj : null;
  142. }
  143. private int Add(IIpcService obj)
  144. {
  145. return _domainObjects.Add(obj);
  146. }
  147. private bool Delete(int id)
  148. {
  149. object obj = _domainObjects.Delete(id);
  150. if (obj is IDisposable disposableObj)
  151. {
  152. disposableObj.Dispose();
  153. }
  154. return obj != null;
  155. }
  156. private IIpcService GetObject(int id)
  157. {
  158. return _domainObjects.GetData<IIpcService>(id);
  159. }
  160. }
  161. }