IpcService.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.HLE.Exceptions;
  3. using Ryujinx.HLE.HOS.Ipc;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.IO;
  7. using System.Reflection;
  8. using System.Linq;
  9. namespace Ryujinx.HLE.HOS.Services
  10. {
  11. abstract class IpcService : IIpcService
  12. {
  13. public IReadOnlyDictionary<int, MethodInfo> Commands { get; }
  14. public ServerBase Server { get; private set; }
  15. private IpcService _parent;
  16. private IdDictionary _domainObjects;
  17. private int _selfId;
  18. private bool _isDomain;
  19. public IpcService(ServerBase server = null)
  20. {
  21. Commands = Assembly.GetExecutingAssembly().GetTypes()
  22. .Where(type => type == GetType())
  23. .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
  24. .SelectMany(methodInfo => methodInfo.GetCustomAttributes(typeof(CommandAttribute))
  25. .Select(command => (((CommandAttribute)command).Id, methodInfo)))
  26. .ToDictionary(command => command.Id, command => command.methodInfo);
  27. Server = server;
  28. _parent = this;
  29. _domainObjects = new IdDictionary();
  30. _selfId = -1;
  31. }
  32. public int ConvertToDomain()
  33. {
  34. if (_selfId == -1)
  35. {
  36. _selfId = _domainObjects.Add(this);
  37. }
  38. _isDomain = true;
  39. return _selfId;
  40. }
  41. public void ConvertToSession()
  42. {
  43. _isDomain = false;
  44. }
  45. public void CallMethod(ServiceCtx context)
  46. {
  47. IIpcService service = this;
  48. if (_isDomain)
  49. {
  50. int domainWord0 = context.RequestData.ReadInt32();
  51. int domainObjId = context.RequestData.ReadInt32();
  52. int domainCmd = (domainWord0 >> 0) & 0xff;
  53. int inputObjCount = (domainWord0 >> 8) & 0xff;
  54. int dataPayloadSize = (domainWord0 >> 16) & 0xffff;
  55. context.RequestData.BaseStream.Seek(0x10 + dataPayloadSize, SeekOrigin.Begin);
  56. for (int index = 0; index < inputObjCount; index++)
  57. {
  58. context.Request.ObjectIds.Add(context.RequestData.ReadInt32());
  59. }
  60. context.RequestData.BaseStream.Seek(0x10, SeekOrigin.Begin);
  61. if (domainCmd == 1)
  62. {
  63. service = GetObject(domainObjId);
  64. context.ResponseData.Write(0L);
  65. context.ResponseData.Write(0L);
  66. }
  67. else if (domainCmd == 2)
  68. {
  69. Delete(domainObjId);
  70. context.ResponseData.Write(0L);
  71. return;
  72. }
  73. else
  74. {
  75. throw new NotImplementedException($"Domain command: {domainCmd}");
  76. }
  77. }
  78. long sfciMagic = context.RequestData.ReadInt64();
  79. int commandId = (int)context.RequestData.ReadInt64();
  80. bool serviceExists = service.Commands.TryGetValue(commandId, out MethodInfo processRequest);
  81. if (ServiceConfiguration.IgnoreMissingServices || serviceExists)
  82. {
  83. ResultCode result = ResultCode.Success;
  84. context.ResponseData.BaseStream.Seek(_isDomain ? 0x20 : 0x10, SeekOrigin.Begin);
  85. if (serviceExists)
  86. {
  87. Logger.Debug?.Print(LogClass.KernelIpc, $"{service.GetType().Name}: {processRequest.Name}");
  88. result = (ResultCode)processRequest.Invoke(service, new object[] { context });
  89. }
  90. else
  91. {
  92. string serviceName;
  93. DummyService dummyService = service as DummyService;
  94. serviceName = (dummyService == null) ? service.GetType().FullName : dummyService.ServiceName;
  95. Logger.Warning?.Print(LogClass.KernelIpc, $"Missing service {serviceName}: {commandId} ignored");
  96. }
  97. if (_isDomain)
  98. {
  99. foreach (int id in context.Response.ObjectIds)
  100. {
  101. context.ResponseData.Write(id);
  102. }
  103. context.ResponseData.BaseStream.Seek(0, SeekOrigin.Begin);
  104. context.ResponseData.Write(context.Response.ObjectIds.Count);
  105. }
  106. context.ResponseData.BaseStream.Seek(_isDomain ? 0x10 : 0, SeekOrigin.Begin);
  107. context.ResponseData.Write(IpcMagic.Sfco);
  108. context.ResponseData.Write((long)result);
  109. }
  110. else
  111. {
  112. string dbgMessage = $"{service.GetType().FullName}: {commandId}";
  113. throw new ServiceNotImplementedException(service, context, dbgMessage);
  114. }
  115. }
  116. protected void MakeObject(ServiceCtx context, IpcService obj)
  117. {
  118. obj.TrySetServer(_parent.Server);
  119. if (_parent._isDomain)
  120. {
  121. obj._parent = _parent;
  122. context.Response.ObjectIds.Add(_parent.Add(obj));
  123. }
  124. else
  125. {
  126. context.Device.System.KernelContext.Syscall.CreateSession(false, 0, out int serverSessionHandle, out int clientSessionHandle);
  127. obj.Server.AddSessionObj(serverSessionHandle, obj);
  128. context.Response.HandleDesc = IpcHandleDesc.MakeMove(clientSessionHandle);
  129. }
  130. }
  131. protected T GetObject<T>(ServiceCtx context, int index) where T : IpcService
  132. {
  133. int objId = context.Request.ObjectIds[index];
  134. IIpcService obj = _parent.GetObject(objId);
  135. return obj is T t ? t : null;
  136. }
  137. public bool TrySetServer(ServerBase newServer)
  138. {
  139. if (Server == null)
  140. {
  141. Server = newServer;
  142. return true;
  143. }
  144. return false;
  145. }
  146. private int Add(IIpcService obj)
  147. {
  148. return _domainObjects.Add(obj);
  149. }
  150. private bool Delete(int id)
  151. {
  152. object obj = _domainObjects.Delete(id);
  153. if (obj is IDisposable disposableObj)
  154. {
  155. disposableObj.Dispose();
  156. }
  157. return obj != null;
  158. }
  159. private IIpcService GetObject(int id)
  160. {
  161. return _domainObjects.GetData<IIpcService>(id);
  162. }
  163. public void SetParent(IpcService parent)
  164. {
  165. _parent = parent._parent;
  166. }
  167. }
  168. }