IUserInterface.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.HLE.Exceptions;
  3. using Ryujinx.HLE.HOS.Ipc;
  4. using Ryujinx.HLE.HOS.Kernel;
  5. using Ryujinx.HLE.HOS.Kernel.Common;
  6. using Ryujinx.HLE.HOS.Kernel.Ipc;
  7. using System;
  8. using System.Collections.Concurrent;
  9. using System.Collections.Generic;
  10. using System.IO;
  11. using System.Linq;
  12. using System.Reflection;
  13. namespace Ryujinx.HLE.HOS.Services.Sm
  14. {
  15. class IUserInterface : IpcService
  16. {
  17. private Dictionary<string, Type> _services;
  18. private readonly ConcurrentDictionary<string, KPort> _registeredServices;
  19. private readonly ServerBase _commonServer;
  20. private bool _isInitialized;
  21. public IUserInterface(KernelContext context)
  22. {
  23. _registeredServices = new ConcurrentDictionary<string, KPort>();
  24. _services = Assembly.GetExecutingAssembly().GetTypes()
  25. .SelectMany(type => type.GetCustomAttributes(typeof(ServiceAttribute), true)
  26. .Select(service => (((ServiceAttribute)service).Name, type)))
  27. .ToDictionary(service => service.Name, service => service.type);
  28. TrySetServer(new ServerBase(context, "SmServer") { SmObject = this });
  29. _commonServer = new ServerBase(context, "CommonServer");
  30. }
  31. [CommandHipc(0)]
  32. [CommandTipc(0)] // 12.0.0+
  33. // Initialize(pid, u64 reserved)
  34. public ResultCode Initialize(ServiceCtx context)
  35. {
  36. _isInitialized = true;
  37. return ResultCode.Success;
  38. }
  39. [CommandHipc(1)]
  40. [CommandTipc(1)] // 12.0.0+
  41. // GetService(ServiceName name) -> handle<move, session>
  42. public ResultCode GetService(ServiceCtx context)
  43. {
  44. if (!_isInitialized)
  45. {
  46. return ResultCode.NotInitialized;
  47. }
  48. string name = ReadName(context);
  49. if (name == string.Empty)
  50. {
  51. return ResultCode.InvalidName;
  52. }
  53. KSession session = new KSession(context.Device.System.KernelContext);
  54. if (_registeredServices.TryGetValue(name, out KPort port))
  55. {
  56. KernelResult result = port.EnqueueIncomingSession(session.ServerSession);
  57. if (result != KernelResult.Success)
  58. {
  59. throw new InvalidOperationException($"Session enqueue on port returned error \"{result}\".");
  60. }
  61. }
  62. else
  63. {
  64. if (_services.TryGetValue(name, out Type type))
  65. {
  66. ServiceAttribute serviceAttribute = (ServiceAttribute)type.GetCustomAttributes(typeof(ServiceAttribute)).First(service => ((ServiceAttribute)service).Name == name);
  67. IpcService service = serviceAttribute.Parameter != null
  68. ? (IpcService)Activator.CreateInstance(type, context, serviceAttribute.Parameter)
  69. : (IpcService)Activator.CreateInstance(type, context);
  70. service.TrySetServer(_commonServer);
  71. service.Server.AddSessionObj(session.ServerSession, service);
  72. }
  73. else
  74. {
  75. if (ServiceConfiguration.IgnoreMissingServices)
  76. {
  77. Logger.Warning?.Print(LogClass.Service, $"Missing service {name} ignored");
  78. }
  79. else
  80. {
  81. throw new NotImplementedException(name);
  82. }
  83. }
  84. }
  85. if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
  86. {
  87. throw new InvalidOperationException("Out of handles!");
  88. }
  89. session.ServerSession.DecrementReferenceCount();
  90. session.ClientSession.DecrementReferenceCount();
  91. context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
  92. return ResultCode.Success;
  93. }
  94. [CommandHipc(2)]
  95. // RegisterService(ServiceName name, u8 isLight, u32 maxHandles) -> handle<move, port>
  96. public ResultCode RegisterServiceHipc(ServiceCtx context)
  97. {
  98. if (!_isInitialized)
  99. {
  100. return ResultCode.NotInitialized;
  101. }
  102. long namePosition = context.RequestData.BaseStream.Position;
  103. string name = ReadName(context);
  104. context.RequestData.BaseStream.Seek(namePosition + 8, SeekOrigin.Begin);
  105. bool isLight = (context.RequestData.ReadInt32() & 1) != 0;
  106. int maxSessions = context.RequestData.ReadInt32();
  107. return RegisterService(context, name, isLight, maxSessions);
  108. }
  109. [CommandTipc(2)] // 12.0.0+
  110. // RegisterService(ServiceName name, u32 maxHandles, u8 isLight) -> handle<move, port>
  111. public ResultCode RegisterServiceTipc(ServiceCtx context)
  112. {
  113. if (!_isInitialized)
  114. {
  115. return ResultCode.NotInitialized;
  116. }
  117. long namePosition = context.RequestData.BaseStream.Position;
  118. string name = ReadName(context);
  119. context.RequestData.BaseStream.Seek(namePosition + 8, SeekOrigin.Begin);
  120. int maxSessions = context.RequestData.ReadInt32();
  121. bool isLight = (context.RequestData.ReadInt32() & 1) != 0;
  122. return RegisterService(context, name, isLight, maxSessions);
  123. }
  124. private ResultCode RegisterService(ServiceCtx context, string name, bool isLight, int maxSessions)
  125. {
  126. if (string.IsNullOrEmpty(name))
  127. {
  128. return ResultCode.InvalidName;
  129. }
  130. Logger.Info?.Print(LogClass.ServiceSm, $"Register \"{name}\".");
  131. KPort port = new KPort(context.Device.System.KernelContext, maxSessions, isLight, 0);
  132. if (!_registeredServices.TryAdd(name, port))
  133. {
  134. return ResultCode.AlreadyRegistered;
  135. }
  136. if (context.Process.HandleTable.GenerateHandle(port.ServerPort, out int handle) != KernelResult.Success)
  137. {
  138. throw new InvalidOperationException("Out of handles!");
  139. }
  140. context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
  141. return ResultCode.Success;
  142. }
  143. [CommandHipc(3)]
  144. [CommandTipc(3)] // 12.0.0+
  145. // UnregisterService(ServiceName name)
  146. public ResultCode UnregisterService(ServiceCtx context)
  147. {
  148. if (!_isInitialized)
  149. {
  150. return ResultCode.NotInitialized;
  151. }
  152. long namePosition = context.RequestData.BaseStream.Position;
  153. string name = ReadName(context);
  154. context.RequestData.BaseStream.Seek(namePosition + 8, SeekOrigin.Begin);
  155. bool isLight = (context.RequestData.ReadInt32() & 1) != 0;
  156. int maxSessions = context.RequestData.ReadInt32();
  157. if (string.IsNullOrEmpty(name))
  158. {
  159. return ResultCode.InvalidName;
  160. }
  161. if (!_registeredServices.TryRemove(name, out _))
  162. {
  163. return ResultCode.NotRegistered;
  164. }
  165. return ResultCode.Success;
  166. }
  167. private static string ReadName(ServiceCtx context)
  168. {
  169. string name = string.Empty;
  170. for (int index = 0; index < 8 &&
  171. context.RequestData.BaseStream.Position <
  172. context.RequestData.BaseStream.Length; index++)
  173. {
  174. byte chr = context.RequestData.ReadByte();
  175. if (chr >= 0x20 && chr < 0x7f)
  176. {
  177. name += (char)chr;
  178. }
  179. }
  180. return name;
  181. }
  182. }
  183. }