From 08831eecf77cedd3c4192ebab5a9c485fb15d51e Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Wed, 4 Jan 2023 19:15:45 -0300
Subject: IPC refactor part 3+4: New server HIPC message processor (#4188)

* IPC refactor part 3 + 4: New server HIPC message processor with source generator based serialization

* Make types match on calls to AlignUp/AlignDown

* Formatting

* Address some PR feedback

* Move BitfieldExtensions to Ryujinx.Common.Utilities and consolidate implementations

* Rename Reader/Writer to SpanReader/SpanWriter and move to Ryujinx.Common.Memory

* Implement EventType

* Address more PR feedback

* Log request processing errors since they are not normal

* Rename waitable to multiwait and add missing lock

* PR feedback

* Ac_K PR feedback
---
 .../Sdk/Sf/Cmif/ServiceDispatchTableBase.cs        | 90 ++++++++++++++++++++++
 1 file changed, 90 insertions(+)
 create mode 100644 Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs

(limited to 'Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs')

diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs
new file mode 100644
index 00000000..a0e28ca8
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs
@@ -0,0 +1,90 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Sf.Hipc;
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Horizon.Sdk.Sf.Cmif
+{
+    abstract class ServiceDispatchTableBase
+    {
+        private const uint MaxCmifVersion = 1;
+
+        public abstract Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData);
+
+        protected Result ProcessMessageImpl(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData, IReadOnlyDictionary<int, CommandHandler> entries, string objectName)
+        {
+            if (inRawData.Length < Unsafe.SizeOf<CmifInHeader>())
+            {
+                Logger.Warning?.Print(LogClass.KernelIpc, $"Request message size 0x{inRawData.Length:X} is invalid");
+
+                return SfResult.InvalidHeaderSize;
+            }
+
+            CmifInHeader inHeader = MemoryMarshal.Cast<byte, CmifInHeader>(inRawData)[0];
+
+            if (inHeader.Magic != CmifMessage.CmifInHeaderMagic || inHeader.Version > MaxCmifVersion)
+            {
+                Logger.Warning?.Print(LogClass.KernelIpc, $"Request message header magic value 0x{inHeader.Magic:X} is invalid");
+
+                return SfResult.InvalidInHeader;
+            }
+
+            ReadOnlySpan<byte> inMessageRawData = inRawData[Unsafe.SizeOf<CmifInHeader>()..];
+            uint commandId = inHeader.CommandId;
+
+            var outHeader = Span<CmifOutHeader>.Empty;
+
+            if (!entries.TryGetValue((int)commandId, out var commandHandler))
+            {
+                Logger.Warning?.Print(LogClass.KernelIpc, $"{objectName} command ID 0x{commandId:X} is not implemented");
+
+                if (HorizonStatic.Options.IgnoreMissingServices)
+                {
+                    // If ignore missing services is enabled, just pretend that everything is fine.
+                    var response = PrepareForStubReply(ref context, out Span<byte> outRawData);
+                    CommandHandler.GetCmifOutHeaderPointer(ref outHeader, ref outRawData);
+                    outHeader[0] = new CmifOutHeader() { Magic = CmifMessage.CmifOutHeaderMagic, Result = Result.Success };
+
+                    return Result.Success;
+                }
+
+                return SfResult.UnknownCommandId;
+            }
+
+            Logger.Trace?.Print(LogClass.KernelIpc, $"{objectName}.{commandHandler.MethodName} called");
+
+            Result commandResult = commandHandler.Invoke(ref outHeader, ref context, inMessageRawData);
+
+            if (commandResult.Module == SfResult.ModuleId ||
+                commandResult.Module == HipcResult.ModuleId)
+            {
+                Logger.Warning?.Print(LogClass.KernelIpc, $"{commandHandler.MethodName} returned error {commandResult}");
+            }
+
+            if (SfResult.RequestContextChanged(commandResult))
+            {
+                return commandResult;
+            }
+
+            if (outHeader.IsEmpty)
+            {
+                commandResult.AbortOnSuccess();
+                return commandResult;
+            }
+
+            outHeader[0] = new CmifOutHeader() { Magic = CmifMessage.CmifOutHeaderMagic, Result = commandResult };
+
+            return Result.Success;
+        }
+
+        private static HipcMessageData PrepareForStubReply(scoped ref ServiceDispatchContext context, out Span<byte> outRawData)
+        {
+            var response = HipcMessage.WriteResponse(context.OutMessageBuffer, 0, 0x20 / sizeof(uint), 0, 0);
+            outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords);
+            return response;
+        }
+    }
+}
-- 
cgit v1.2.3-70-g09d2