IpcService.cs 6.1 KB

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