IpcService.cs 6.8 KB

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