ServiceDispatchTableBase.cs 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Horizon.Common;
  3. using Ryujinx.Horizon.Sdk.Sf.Hipc;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Runtime.CompilerServices;
  7. using System.Runtime.InteropServices;
  8. namespace Ryujinx.Horizon.Sdk.Sf.Cmif
  9. {
  10. abstract class ServiceDispatchTableBase
  11. {
  12. private const uint MaxCmifVersion = 1;
  13. public abstract Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData);
  14. protected static Result ProcessMessageImpl(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData, IReadOnlyDictionary<int, CommandHandler> entries, string objectName)
  15. {
  16. if (inRawData.Length < Unsafe.SizeOf<CmifInHeader>())
  17. {
  18. Logger.Warning?.Print(LogClass.KernelIpc, $"Request message size 0x{inRawData.Length:X} is invalid");
  19. return SfResult.InvalidHeaderSize;
  20. }
  21. CmifInHeader inHeader = MemoryMarshal.Cast<byte, CmifInHeader>(inRawData)[0];
  22. if (inHeader.Magic != CmifMessage.CmifInHeaderMagic || inHeader.Version > MaxCmifVersion)
  23. {
  24. Logger.Warning?.Print(LogClass.KernelIpc, $"Request message header magic value 0x{inHeader.Magic:X} is invalid");
  25. return SfResult.InvalidInHeader;
  26. }
  27. ReadOnlySpan<byte> inMessageRawData = inRawData[Unsafe.SizeOf<CmifInHeader>()..];
  28. uint commandId = inHeader.CommandId;
  29. var outHeader = Span<CmifOutHeader>.Empty;
  30. if (!entries.TryGetValue((int)commandId, out var commandHandler))
  31. {
  32. if (HorizonStatic.Options.IgnoreMissingServices)
  33. {
  34. // If ignore missing services is enabled, just pretend that everything is fine.
  35. PrepareForStubReply(ref context, out Span<byte> outRawData);
  36. CommandHandler.GetCmifOutHeaderPointer(ref outHeader, ref outRawData);
  37. outHeader[0] = new CmifOutHeader { Magic = CmifMessage.CmifOutHeaderMagic, Result = Result.Success };
  38. Logger.Warning?.Print(LogClass.Service, $"Missing service {objectName} (command ID: {commandId}) ignored");
  39. return Result.Success;
  40. }
  41. else if (HorizonStatic.Options.ThrowOnInvalidCommandIds)
  42. {
  43. throw new NotImplementedException($"{objectName} command ID: {commandId} is not implemented");
  44. }
  45. return SfResult.UnknownCommandId;
  46. }
  47. Logger.Trace?.Print(LogClass.KernelIpc, $"{objectName}.{commandHandler.MethodName} called");
  48. Result commandResult = commandHandler.Invoke(ref outHeader, ref context, inMessageRawData);
  49. if (commandResult.Module == SfResult.ModuleId ||
  50. commandResult.Module == HipcResult.ModuleId)
  51. {
  52. Logger.Warning?.Print(LogClass.KernelIpc, $"{commandHandler.MethodName} returned error {commandResult}");
  53. }
  54. if (SfResult.RequestContextChanged(commandResult))
  55. {
  56. return commandResult;
  57. }
  58. if (outHeader.IsEmpty)
  59. {
  60. commandResult.AbortOnSuccess();
  61. return commandResult;
  62. }
  63. outHeader[0] = new CmifOutHeader { Magic = CmifMessage.CmifOutHeaderMagic, Result = commandResult };
  64. return Result.Success;
  65. }
  66. private static void PrepareForStubReply(scoped ref ServiceDispatchContext context, out Span<byte> outRawData)
  67. {
  68. var response = HipcMessage.WriteResponse(context.OutMessageBuffer, 0, 0x20 / sizeof(uint), 0, 0);
  69. outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords);
  70. }
  71. }
  72. }