IUserInterface.cs 6.9 KB

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