diff options
Diffstat (limited to 'Ryujinx.Horizon/Sdk/Sf/Hipc')
21 files changed, 1601 insertions, 0 deletions
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs new file mode 100644 index 00000000..deac524c --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs @@ -0,0 +1,89 @@ +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + static class Api + { + public const int TlsMessageBufferSize = 0x100; + + public static Result Receive(out ReceiveResult recvResult, int sessionHandle, Span<byte> messageBuffer) + { + Result result = ReceiveImpl(sessionHandle, messageBuffer); + + if (result == KernelResult.PortRemoteClosed) + { + recvResult = ReceiveResult.Closed; + + return Result.Success; + } + else if (result == KernelResult.ReceiveListBroken) + { + recvResult = ReceiveResult.NeedsRetry; + + return Result.Success; + } + + recvResult = ReceiveResult.Success; + + return result; + } + + private static Result ReceiveImpl(int sessionHandle, Span<byte> messageBuffer) + { + Span<int> handles = stackalloc int[1]; + + handles[0] = sessionHandle; + + var tlsSpan = HorizonStatic.AddressSpace.GetSpan(HorizonStatic.ThreadContext.TlsAddress, TlsMessageBufferSize); + + if (messageBuffer == tlsSpan) + { + return HorizonStatic.Syscall.ReplyAndReceive(out _, handles, 0, -1L); + } + else + { + throw new NotImplementedException(); + } + } + + public static Result Reply(int sessionHandle, ReadOnlySpan<byte> messageBuffer) + { + Result result = ReplyImpl(sessionHandle, messageBuffer); + + result.AbortUnless(KernelResult.TimedOut, KernelResult.PortRemoteClosed); + + return Result.Success; + } + + private static Result ReplyImpl(int sessionHandle, ReadOnlySpan<byte> messageBuffer) + { + Span<int> handles = stackalloc int[1]; + + handles[0] = sessionHandle; + + var tlsSpan = HorizonStatic.AddressSpace.GetSpan(HorizonStatic.ThreadContext.TlsAddress, TlsMessageBufferSize); + + if (messageBuffer == tlsSpan) + { + return HorizonStatic.Syscall.ReplyAndReceive(out _, handles, sessionHandle, 0); + } + else + { + throw new NotImplementedException(); + } + } + + public static Result CreateSession(out int serverHandle, out int clientHandle) + { + Result result = HorizonStatic.Syscall.CreateSession(out serverHandle, out clientHandle, false, null); + + if (result == KernelResult.OutOfResource) + { + return HipcResult.OutOfSessions; + } + + return result; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs new file mode 100644 index 00000000..cdb50b57 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs @@ -0,0 +1,65 @@ +using Ryujinx.Common.Utilities; +using Ryujinx.Horizon.Sdk.Sf.Cmif; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + struct Header + { + private uint _word0; + private uint _word1; + + public CommandType Type + { + get => (CommandType)_word0.Extract(0, 16); + set => _word0 = _word0.Insert(0, 16, (uint)value); + } + + public int SendStaticsCount + { + get => (int)_word0.Extract(16, 4); + set => _word0 = _word0.Insert(16, 4, (uint)value); + } + + public int SendBuffersCount + { + get => (int)_word0.Extract(20, 4); + set => _word0 = _word0.Insert(20, 4, (uint)value); + } + + public int ReceiveBuffersCount + { + get => (int)_word0.Extract(24, 4); + set => _word0 = _word0.Insert(24, 4, (uint)value); + } + + public int ExchangeBuffersCount + { + get => (int)_word0.Extract(28, 4); + set => _word0 = _word0.Insert(28, 4, (uint)value); + } + + public int DataWordsCount + { + get => (int)_word1.Extract(0, 10); + set => _word1 = _word1.Insert(0, 10, (uint)value); + } + + public int ReceiveStaticMode + { + get => (int)_word1.Extract(10, 4); + set => _word1 = _word1.Insert(10, 4, (uint)value); + } + + public int ReceiveListOffset + { + get => (int)_word1.Extract(20, 11); + set => _word1 = _word1.Insert(20, 11, (uint)value); + } + + public bool HasSpecialHeader + { + get => _word1.Extract(31); + set => _word1 = _word1.Insert(31, value); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs new file mode 100644 index 00000000..7778d5bc --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + struct HipcBufferDescriptor + { +#pragma warning disable CS0649 + private uint _sizeLow; + private uint _addressLow; + private uint _word2; +#pragma warning restore CS0649 + + public ulong Address => _addressLow | (((ulong)_word2 << 4) & 0xf00000000UL) | (((ulong)_word2 << 34) & 0x7000000000UL); + public ulong Size => _sizeLow | ((ulong)_word2 << 8) & 0xf00000000UL; + public HipcBufferMode Mode => (HipcBufferMode)(_word2 & 3); + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs new file mode 100644 index 00000000..594af2c8 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs @@ -0,0 +1,17 @@ +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + [Flags] + enum HipcBufferFlags : byte + { + In = 1 << 0, + Out = 1 << 1, + MapAlias = 1 << 2, + Pointer = 1 << 3, + FixedSize = 1 << 4, + AutoSelect = 1 << 5, + MapTransferAllowsNonSecure = 1 << 6, + MapTransferAllowsNonDevice = 1 << 7 + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs new file mode 100644 index 00000000..4ef6374b --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + enum HipcBufferMode + { + Normal = 0, + NonSecure = 1, + Invalid = 2, + NonDevice = 3 + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs new file mode 100644 index 00000000..ea2ec650 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs @@ -0,0 +1,115 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + partial class HipcManager : IServiceObject + { + private readonly ServerDomainSessionManager _manager; + private readonly ServerSession _session; + + public HipcManager(ServerDomainSessionManager manager, ServerSession session) + { + _manager = manager; + _session = session; + } + + [CmifCommand(0)] + public Result ConvertCurrentObjectToDomain(out int objectId) + { + objectId = 0; + + var domain = _manager.Domain.AllocateDomainServiceObject(); + if (domain == null) + { + return HipcResult.OutOfDomains; + } + + bool succeeded = false; + + try + { + Span<int> objectIds = stackalloc int[1]; + + Result result = domain.ReserveIds(objectIds); + + if (result.IsFailure) + { + return result; + } + + objectId = objectIds[0]; + succeeded = true; + } + finally + { + if (!succeeded) + { + ServerDomainManager.DestroyDomainServiceObject(domain); + } + } + + domain.RegisterObject(objectId, _session.ServiceObjectHolder); + _session.ServiceObjectHolder = new ServiceObjectHolder(domain); + + return Result.Success; + } + + [CmifCommand(1)] + public Result CopyFromCurrentDomain([MoveHandle] out int clientHandle, int objectId) + { + clientHandle = 0; + + if (!(_session.ServiceObjectHolder.ServiceObject is DomainServiceObject domain)) + { + return HipcResult.TargetNotDomain; + } + + var obj = domain.GetObject(objectId); + if (obj == null) + { + return HipcResult.DomainObjectNotFound; + } + + Api.CreateSession(out int serverHandle, out clientHandle).AbortOnFailure(); + _manager.RegisterSession(serverHandle, obj).AbortOnFailure(); + + return Result.Success; + } + + [CmifCommand(2)] + public Result CloneCurrentObject([MoveHandle] out int clientHandle) + { + return CloneCurrentObjectImpl(out clientHandle, _manager); + } + + [CmifCommand(3)] + public void QueryPointerBufferSize(out ushort size) + { + size = (ushort)_session.PointerBuffer.Size; + } + + [CmifCommand(4)] + public Result CloneCurrentObjectEx([MoveHandle] out int clientHandle, uint tag) + { + return CloneCurrentObjectImpl(out clientHandle, _manager.GetSessionManagerByTag(tag)); + } + + private Result CloneCurrentObjectImpl(out int clientHandle, ServerSessionManager manager) + { + clientHandle = 0; + + var clone = _session.ServiceObjectHolder.Clone(); + if (clone == null) + { + return HipcResult.DomainObjectNotFound; + } + + Api.CreateSession(out int serverHandle, out clientHandle).AbortOnFailure(); + manager.RegisterSession(serverHandle, clone).AbortOnFailure(); + + return Result.Success; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs new file mode 100644 index 00000000..3017f404 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs @@ -0,0 +1,222 @@ +using Ryujinx.Common; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + ref struct HipcMessage + { + public const int AutoReceiveStatic = byte.MaxValue; + + public HipcMetadata Meta; + public HipcMessageData Data; + public ulong Pid; + + public HipcMessage(Span<byte> data) + { + int initialLength = data.Length; + + Header header = MemoryMarshal.Cast<byte, Header>(data)[0]; + + data = data.Slice(Unsafe.SizeOf<Header>()); + + int receiveStaticsCount = 0; + ulong pid = 0; + + if (header.ReceiveStaticMode != 0) + { + if (header.ReceiveStaticMode == 2) + { + receiveStaticsCount = AutoReceiveStatic; + } + else if (header.ReceiveStaticMode > 2) + { + receiveStaticsCount = header.ReceiveStaticMode - 2; + } + } + + SpecialHeader specialHeader = default; + + if (header.HasSpecialHeader) + { + specialHeader = MemoryMarshal.Cast<byte, SpecialHeader>(data)[0]; + + data = data.Slice(Unsafe.SizeOf<SpecialHeader>()); + + if (specialHeader.SendPid) + { + pid = MemoryMarshal.Cast<byte, ulong>(data)[0]; + + data = data.Slice(sizeof(ulong)); + } + } + + Meta = new HipcMetadata() + { + Type = (int)header.Type, + SendStaticsCount = header.SendStaticsCount, + SendBuffersCount = header.SendBuffersCount, + ReceiveBuffersCount = header.ReceiveBuffersCount, + ExchangeBuffersCount = header.ExchangeBuffersCount, + DataWordsCount = header.DataWordsCount, + ReceiveStaticsCount = receiveStaticsCount, + SendPid = specialHeader.SendPid, + CopyHandlesCount = specialHeader.CopyHandlesCount, + MoveHandlesCount = specialHeader.MoveHandlesCount + }; + + Data = CreateMessageData(Meta, data, initialLength); + Pid = pid; + } + + public static HipcMessageData WriteResponse( + Span<byte> destination, + int sendStaticCount, + int dataWordsCount, + int copyHandlesCount, + int moveHandlesCount) + { + return WriteMessage(destination, new HipcMetadata() + { + SendStaticsCount = sendStaticCount, + DataWordsCount = dataWordsCount, + CopyHandlesCount = copyHandlesCount, + MoveHandlesCount = moveHandlesCount + }); + } + + public static HipcMessageData WriteMessage(Span<byte> destination, HipcMetadata meta) + { + int initialLength = destination.Length; + + bool hasSpecialHeader = meta.SendPid || meta.CopyHandlesCount != 0 || meta.MoveHandlesCount != 0; + + MemoryMarshal.Cast<byte, Header>(destination)[0] = new Header() + { + Type = (CommandType)meta.Type, + SendStaticsCount = meta.SendStaticsCount, + SendBuffersCount = meta.SendBuffersCount, + ReceiveBuffersCount = meta.ReceiveBuffersCount, + ExchangeBuffersCount = meta.ExchangeBuffersCount, + DataWordsCount = meta.DataWordsCount, + ReceiveStaticMode = meta.ReceiveStaticsCount != 0 ? (meta.ReceiveStaticsCount != AutoReceiveStatic ? meta.ReceiveStaticsCount + 2 : 2) : 0, + HasSpecialHeader = hasSpecialHeader + }; + + destination = destination.Slice(Unsafe.SizeOf<Header>()); + + if (hasSpecialHeader) + { + MemoryMarshal.Cast<byte, SpecialHeader>(destination)[0] = new SpecialHeader() + { + SendPid = meta.SendPid, + CopyHandlesCount = meta.CopyHandlesCount, + MoveHandlesCount = meta.MoveHandlesCount + }; + + destination = destination.Slice(Unsafe.SizeOf<SpecialHeader>()); + + if (meta.SendPid) + { + destination = destination.Slice(sizeof(ulong)); + } + } + + return CreateMessageData(meta, destination, initialLength); + } + + private static HipcMessageData CreateMessageData(HipcMetadata meta, Span<byte> data, int initialLength) + { + Span<int> copyHandles = Span<int>.Empty; + + if (meta.CopyHandlesCount != 0) + { + copyHandles = MemoryMarshal.Cast<byte, int>(data).Slice(0, meta.CopyHandlesCount); + + data = data.Slice(meta.CopyHandlesCount * sizeof(int)); + } + + Span<int> moveHandles = Span<int>.Empty; + + if (meta.MoveHandlesCount != 0) + { + moveHandles = MemoryMarshal.Cast<byte, int>(data).Slice(0, meta.MoveHandlesCount); + + data = data.Slice(meta.MoveHandlesCount * sizeof(int)); + } + + Span<HipcStaticDescriptor> sendStatics = Span<HipcStaticDescriptor>.Empty; + + if (meta.SendStaticsCount != 0) + { + sendStatics = MemoryMarshal.Cast<byte, HipcStaticDescriptor>(data).Slice(0, meta.SendStaticsCount); + + data = data.Slice(meta.SendStaticsCount * Unsafe.SizeOf<HipcStaticDescriptor>()); + } + + Span<HipcBufferDescriptor> sendBuffers = Span<HipcBufferDescriptor>.Empty; + + if (meta.SendBuffersCount != 0) + { + sendBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data).Slice(0, meta.SendBuffersCount); + + data = data.Slice(meta.SendBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>()); + } + + Span<HipcBufferDescriptor> receiveBuffers = Span<HipcBufferDescriptor>.Empty; + + if (meta.ReceiveBuffersCount != 0) + { + receiveBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data).Slice(0, meta.ReceiveBuffersCount); + + data = data.Slice(meta.ReceiveBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>()); + } + + Span<HipcBufferDescriptor> exchangeBuffers = Span<HipcBufferDescriptor>.Empty; + + if (meta.ExchangeBuffersCount != 0) + { + exchangeBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data).Slice(0, meta.ExchangeBuffersCount); + + data = data.Slice(meta.ExchangeBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>()); + } + + Span<uint> dataWords = Span<uint>.Empty; + + if (meta.DataWordsCount != 0) + { + int dataOffset = initialLength - data.Length; + int dataOffsetAligned = BitUtils.AlignUp(dataOffset, 0x10); + + int padding = (dataOffsetAligned - dataOffset) / sizeof(uint); + + dataWords = MemoryMarshal.Cast<byte, uint>(data).Slice(padding, meta.DataWordsCount - padding); + + data = data.Slice(meta.DataWordsCount * sizeof(uint)); + } + + Span<HipcReceiveListEntry> receiveList = Span<HipcReceiveListEntry>.Empty; + + if (meta.ReceiveStaticsCount != 0) + { + int receiveListSize = meta.ReceiveStaticsCount == AutoReceiveStatic ? 1 : meta.ReceiveStaticsCount; + + receiveList = MemoryMarshal.Cast<byte, HipcReceiveListEntry>(data).Slice(0, receiveListSize); + } + + return new HipcMessageData() + { + SendStatics = sendStatics, + SendBuffers = sendBuffers, + ReceiveBuffers = receiveBuffers, + ExchangeBuffers = exchangeBuffers, + DataWords = dataWords, + ReceiveList = receiveList, + CopyHandles = copyHandles, + MoveHandles = moveHandles + }; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs new file mode 100644 index 00000000..c83c422c --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs @@ -0,0 +1,16 @@ +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + ref struct HipcMessageData + { + public Span<HipcStaticDescriptor> SendStatics; + public Span<HipcBufferDescriptor> SendBuffers; + public Span<HipcBufferDescriptor> ReceiveBuffers; + public Span<HipcBufferDescriptor> ExchangeBuffers; + public Span<uint> DataWords; + public Span<HipcReceiveListEntry> ReceiveList; + public Span<int> CopyHandles; + public Span<int> MoveHandles; + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs new file mode 100644 index 00000000..fe13137a --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs @@ -0,0 +1,16 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + struct HipcMetadata + { + public int Type; + public int SendStaticsCount; + public int SendBuffersCount; + public int ReceiveBuffersCount; + public int ExchangeBuffersCount; + public int DataWordsCount; + public int ReceiveStaticsCount; + public bool SendPid; + public int CopyHandlesCount; + public int MoveHandlesCount; + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs new file mode 100644 index 00000000..94bf0968 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + struct HipcReceiveListEntry + { + private uint _addressLow; + private uint _word1; + + public HipcReceiveListEntry(ulong address, ulong size) + { + _addressLow = (uint)address; + _word1 = (ushort)(address >> 32) | (uint)(size << 16); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs new file mode 100644 index 00000000..ef989a98 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs @@ -0,0 +1,22 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + static class HipcResult + { + public const int ModuleId = 11; + + public static Result OutOfSessionMemory => new Result(ModuleId, 102); + public static Result OutOfSessions => new Result(ModuleId, 131); + public static Result PointerBufferTooSmall => new Result(ModuleId, 141); + public static Result OutOfDomains => new Result(ModuleId, 200); + + public static Result InvalidRequestSize => new Result(ModuleId, 402); + public static Result UnknownCommandType => new Result(ModuleId, 403); + + public static Result InvalidCmifRequest => new Result(ModuleId, 420); + + public static Result TargetNotDomain => new Result(ModuleId, 491); + public static Result DomainObjectNotFound => new Result(ModuleId, 492); + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs new file mode 100644 index 00000000..5cebf47c --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + struct HipcStaticDescriptor + { + private readonly ulong _data; + + public ulong Address => ((((_data >> 2) & 0x70) | ((_data >> 12) & 0xf)) << 32) | (_data >> 32); + public ushort Size => (ushort)(_data >> 16); + public int ReceiveIndex => (int)(_data & 0xf); + + public HipcStaticDescriptor(ulong address, ushort size, int receiveIndex) + { + ulong data = (uint)(receiveIndex & 0xf) | ((uint)size << 16); + + data |= address << 32; + data |= (address >> 20) & 0xf000; + data |= (address >> 30) & 0xffc0; + + _data = data; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs new file mode 100644 index 00000000..e087cb22 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + struct ManagerOptions + { + public static ManagerOptions Default => new ManagerOptions(0, 0, 0, false); + + public int PointerBufferSize { get; } + public int MaxDomains { get; } + public int MaxDomainObjects { get; } + public bool CanDeferInvokeRequest { get; } + + public ManagerOptions(int pointerBufferSize, int maxDomains, int maxDomainObjects, bool canDeferInvokeRequest) + { + PointerBufferSize = pointerBufferSize; + MaxDomains = maxDomains; + MaxDomainObjects = maxDomainObjects; + CanDeferInvokeRequest = canDeferInvokeRequest; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs new file mode 100644 index 00000000..7c380a01 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + enum ReceiveResult + { + Success, + Closed, + NeedsRetry + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs new file mode 100644 index 00000000..923f2d52 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs @@ -0,0 +1,36 @@ +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using Ryujinx.Horizon.Sdk.Sm; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + class Server : MultiWaitHolderOfHandle + { + public int PortIndex { get; } + public int PortHandle { get; } + public ServiceName Name { get; } + public bool Managed { get; } + public ServiceObjectHolder StaticObject { get; } + + public Server( + int portIndex, + int portHandle, + ServiceName name, + bool managed, + ServiceObjectHolder staticHoder) : base(portHandle) + { + PortHandle = portHandle; + Name = name; + Managed = managed; + + if (staticHoder != null) + { + StaticObject = staticHoder; + } + else + { + PortIndex = portIndex; + } + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs new file mode 100644 index 00000000..d920a659 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs @@ -0,0 +1,23 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + class ServerDomainSessionManager : ServerSessionManager + { + public ServerDomainManager Domain { get; } + + public ServerDomainSessionManager(int entryCount, int maxDomains) + { + Domain = new ServerDomainManager(entryCount, maxDomains); + } + + protected override Result DispatchManagerRequest(ServerSession session, Span<byte> inMessage, Span<byte> outMessage) + { + HipcManager hipcManager = new HipcManager(this, session); + + return DispatchRequest(new ServiceObjectHolder(hipcManager), session, inMessage, outMessage); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs new file mode 100644 index 00000000..5bb2de25 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs @@ -0,0 +1,198 @@ +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using Ryujinx.Horizon.Sdk.Sm; +using System; +using System.Collections.Generic; +using System.Numerics; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + class ServerManager : ServerManagerBase, IDisposable + { + private readonly SmApi _sm; + private readonly int _pointerBufferSize; + private readonly bool _canDeferInvokeRequest; + private readonly int _maxSessions; + + private ulong _pointerBuffersBaseAddress; + private ulong _savedMessagesBaseAddress; + + private readonly object _resourceLock; + private readonly ulong[] _sessionAllocationBitmap; + private readonly HashSet<ServerSession> _sessions; + private readonly HashSet<Server> _servers; + + public ServerManager(HeapAllocator allocator, SmApi sm, int maxPorts, ManagerOptions options, int maxSessions) : base(sm, options) + { + _sm = sm; + _pointerBufferSize = options.PointerBufferSize; + _canDeferInvokeRequest = options.CanDeferInvokeRequest; + _maxSessions = maxSessions; + + if (allocator != null) + { + _pointerBuffersBaseAddress = allocator.Allocate((ulong)maxSessions * (ulong)options.PointerBufferSize); + + if (options.CanDeferInvokeRequest) + { + _savedMessagesBaseAddress = allocator.Allocate((ulong)maxSessions * (ulong)Api.TlsMessageBufferSize); + } + } + + _resourceLock = new object(); + _sessionAllocationBitmap = new ulong[(maxSessions + 63) / 64]; + _sessions = new HashSet<ServerSession>(); + _servers = new HashSet<Server>(); + } + + private PointerAndSize GetObjectBySessionIndex(ServerSession session, ulong baseAddress, ulong size) + { + return new PointerAndSize(baseAddress + (ulong)session.SessionIndex * size, size); + } + + protected override ServerSession AllocateSession(int sessionHandle, ServiceObjectHolder obj) + { + int sessionIndex = -1; + + lock (_resourceLock) + { + if (_sessions.Count >= _maxSessions) + { + return null; + } + + for (int i = 0; i <_sessionAllocationBitmap.Length; i++) + { + ref ulong mask = ref _sessionAllocationBitmap[i]; + + if (mask != ulong.MaxValue) + { + int bit = BitOperations.TrailingZeroCount(~mask); + sessionIndex = i * 64 + bit; + mask |= 1UL << bit; + + break; + } + } + + if (sessionIndex == -1) + { + return null; + } + + ServerSession session = new ServerSession(sessionIndex, sessionHandle, obj); + + _sessions.Add(session); + + return session; + } + } + + protected override void FreeSession(ServerSession session) + { + if (session.ServiceObjectHolder.ServiceObject is IDisposable disposableObj) + { + disposableObj.Dispose(); + } + + lock (_resourceLock) + { + _sessionAllocationBitmap[session.SessionIndex / 64] &= ~(1UL << (session.SessionIndex & 63)); + _sessions.Remove(session); + } + } + + protected override Server AllocateServer( + int portIndex, + int portHandle, + ServiceName name, + bool managed, + ServiceObjectHolder staticHoder) + { + lock (_resourceLock) + { + Server server = new Server(portIndex, portHandle, name, managed, staticHoder); + + _servers.Add(server); + + return server; + } + } + + protected override void DestroyServer(Server server) + { + lock (_resourceLock) + { + server.UnlinkFromMultiWaitHolder(); + Os.FinalizeMultiWaitHolder(server); + + if (server.Managed) + { + // We should AbortOnFailure, but sometimes SM is already gone when this is called, + // so let's just ignore potential errors. + _sm.UnregisterService(server.Name); + + HorizonStatic.Syscall.CloseHandle(server.PortHandle); + } + + _servers.Remove(server); + } + } + + protected override PointerAndSize GetSessionPointerBuffer(ServerSession session) + { + if (_pointerBufferSize > 0) + { + return GetObjectBySessionIndex(session, _pointerBuffersBaseAddress, (ulong)_pointerBufferSize); + } + else + { + return PointerAndSize.Empty; + } + } + + protected override PointerAndSize GetSessionSavedMessageBuffer(ServerSession session) + { + if (_canDeferInvokeRequest) + { + return GetObjectBySessionIndex(session, _savedMessagesBaseAddress, Api.TlsMessageBufferSize); + } + else + { + return PointerAndSize.Empty; + } + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + lock (_resourceLock) + { + ServerSession[] sessionsToClose = new ServerSession[_sessions.Count]; + + _sessions.CopyTo(sessionsToClose); + + foreach (ServerSession session in sessionsToClose) + { + CloseSessionImpl(session); + } + + Server[] serversToClose = new Server[_servers.Count]; + + _servers.CopyTo(serversToClose); + + foreach (Server server in serversToClose) + { + DestroyServer(server); + } + } + } + } + + public void Dispose() + { + Dispose(true); + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs new file mode 100644 index 00000000..68cae6bc --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs @@ -0,0 +1,307 @@ +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using Ryujinx.Horizon.Sdk.Sm; +using System; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + class ServerManagerBase : ServerDomainSessionManager + { + private readonly SmApi _sm; + + private bool _canDeferInvokeRequest; + + private readonly MultiWait _multiWait; + private readonly MultiWait _waitList; + + private readonly object _multiWaitSelectionLock; + private readonly object _waitListLock; + + private readonly Event _requestStopEvent; + private readonly Event _notifyEvent; + + private readonly MultiWaitHolderBase _requestStopEventHolder; + private readonly MultiWaitHolderBase _notifyEventHolder; + + private enum UserDataTag + { + Server = 1, + Session = 2 + } + + public ServerManagerBase(SmApi sm, ManagerOptions options) : base(options.MaxDomainObjects, options.MaxDomains) + { + _sm = sm; + _canDeferInvokeRequest = options.CanDeferInvokeRequest; + + _multiWait = new MultiWait(); + _waitList = new MultiWait(); + + _multiWaitSelectionLock = new object(); + _waitListLock = new object(); + + _requestStopEvent = new Event(EventClearMode.ManualClear); + _notifyEvent = new Event(EventClearMode.ManualClear); + + _requestStopEventHolder = new MultiWaitHolderOfEvent(_requestStopEvent); + _multiWait.LinkMultiWaitHolder(_requestStopEventHolder); + _notifyEventHolder = new MultiWaitHolderOfEvent(_notifyEvent); + _multiWait.LinkMultiWaitHolder(_notifyEventHolder); + } + + public void RegisterObjectForServer(IServiceObject staticObject, int portHandle) + { + RegisterServerImpl(0, new ServiceObjectHolder(staticObject), portHandle); + } + + public Result RegisterObjectForServer(IServiceObject staticObject, ServiceName name, int maxSessions) + { + return RegisterServerImpl(0, new ServiceObjectHolder(staticObject), name, maxSessions); + } + + public void RegisterServer(int portIndex, int portHandle) + { + RegisterServerImpl(portIndex, null, portHandle); + } + + public Result RegisterServer(int portIndex, ServiceName name, int maxSessions) + { + return RegisterServerImpl(portIndex, null, name, maxSessions); + } + + private void RegisterServerImpl(int portIndex, ServiceObjectHolder staticHolder, int portHandle) + { + Server server = AllocateServer(portIndex, portHandle, ServiceName.Invalid, managed: false, staticHolder); + RegisterServerImpl(server); + } + + private Result RegisterServerImpl(int portIndex, ServiceObjectHolder staticHolder, ServiceName name, int maxSessions) + { + Result result = _sm.RegisterService(out int portHandle, name, maxSessions, isLight: false); + + if (result.IsFailure) + { + return result; + } + + Server server = AllocateServer(portIndex, portHandle, name, managed: true, staticHolder); + RegisterServerImpl(server); + + return Result.Success; + } + + private void RegisterServerImpl(Server server) + { + server.UserData = UserDataTag.Server; + + _multiWait.LinkMultiWaitHolder(server); + } + + protected virtual Result OnNeedsToAccept(int portIndex, Server server) + { + throw new NotSupportedException(); + } + + public void ServiceRequests() + { + while (WaitAndProcessRequestsImpl()); + } + + public void WaitAndProcessRequests() + { + WaitAndProcessRequestsImpl(); + } + + private bool WaitAndProcessRequestsImpl() + { + try + { + MultiWaitHolder multiWait = WaitSignaled(); + + if (multiWait == null) + { + return false; + } + + DebugUtil.Assert(Process(multiWait).IsSuccess); + + return HorizonStatic.ThreadContext.Running; + } + catch (ThreadTerminatedException) + { + return false; + } + } + + private MultiWaitHolder WaitSignaled() + { + lock (_multiWaitSelectionLock) + { + while (true) + { + ProcessWaitList(); + + MultiWaitHolder selected = _multiWait.WaitAny(); + + if (selected == _requestStopEventHolder) + { + return null; + } + else if (selected == _notifyEventHolder) + { + _notifyEvent.Clear(); + } + else + { + selected.UnlinkFromMultiWaitHolder(); + + return selected; + } + } + } + } + + public void ResumeProcessing() + { + _requestStopEvent.Clear(); + } + + public void RequestStopProcessing() + { + _requestStopEvent.Signal(); + } + + protected override void RegisterSessionToWaitList(ServerSession session) + { + session.HasReceived = false; + session.UserData = UserDataTag.Session; + RegisterToWaitList(session); + } + + private void RegisterToWaitList(MultiWaitHolder holder) + { + lock (_waitListLock) + { + _waitList.LinkMultiWaitHolder(holder); + _notifyEvent.Signal(); + } + } + + private void ProcessWaitList() + { + lock (_waitListLock) + { + _multiWait.MoveAllFrom(_waitList); + } + } + + private Result Process(MultiWaitHolder holder) + { + switch ((UserDataTag)holder.UserData) + { + case UserDataTag.Server: + return ProcessForServer(holder); + case UserDataTag.Session: + return ProcessForSession(holder); + default: + throw new NotImplementedException(((UserDataTag)holder.UserData).ToString()); + } + } + + private Result ProcessForServer(MultiWaitHolder holder) + { + DebugUtil.Assert((UserDataTag)holder.UserData == UserDataTag.Server); + + Server server = (Server)holder; + + try + { + if (server.StaticObject != null) + { + return AcceptSession(server.PortHandle, server.StaticObject.Clone()); + } + else + { + return OnNeedsToAccept(server.PortIndex, server); + } + } + finally + { + RegisterToWaitList(server); + } + } + + private Result ProcessForSession(MultiWaitHolder holder) + { + DebugUtil.Assert((UserDataTag)holder.UserData == UserDataTag.Session); + + ServerSession session = (ServerSession)holder; + + using var tlsMessage = HorizonStatic.AddressSpace.GetWritableRegion(HorizonStatic.ThreadContext.TlsAddress, Api.TlsMessageBufferSize); + + Result result; + + if (_canDeferInvokeRequest) + { + // If the request is deferred, we save the message on a temporary buffer to process it later. + using var savedMessage = HorizonStatic.AddressSpace.GetWritableRegion(session.SavedMessage.Address, (int)session.SavedMessage.Size); + + DebugUtil.Assert(tlsMessage.Memory.Length == savedMessage.Memory.Length); + + if (!session.HasReceived) + { + result = ReceiveRequest(session, tlsMessage.Memory.Span); + + if (result.IsFailure) + { + return result; + } + + session.HasReceived = true; + tlsMessage.Memory.Span.CopyTo(savedMessage.Memory.Span); + } + else + { + savedMessage.Memory.Span.CopyTo(tlsMessage.Memory.Span); + } + + result = ProcessRequest(session, tlsMessage.Memory.Span); + + if (result.IsFailure && !SfResult.Invalidated(result)) + { + return result; + } + } + else + { + if (!session.HasReceived) + { + result = ReceiveRequest(session, tlsMessage.Memory.Span); + + if (result.IsFailure) + { + return result; + } + + session.HasReceived = true; + } + + result = ProcessRequest(session, tlsMessage.Memory.Span); + + if (result.IsFailure) + { + // Those results are not valid because the service does not support deferral. + if (SfResult.RequestDeferred(result) || SfResult.Invalidated(result)) + { + result.AbortOnFailure(); + } + + return result; + } + } + + return Result.Success; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs new file mode 100644 index 00000000..eb98fefd --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs @@ -0,0 +1,23 @@ +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Sf.Cmif; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + class ServerSession : MultiWaitHolderOfHandle + { + public ServiceObjectHolder ServiceObjectHolder { get; set; } + public PointerAndSize PointerBuffer { get; set; } + public PointerAndSize SavedMessage { get; set; } + public int SessionIndex { get; } + public int SessionHandle { get; } + public bool IsClosed { get; set; } + public bool HasReceived { get; set; } + + public ServerSession(int index, int handle, ServiceObjectHolder obj) : base(handle) + { + ServiceObjectHolder = obj; + SessionIndex = index; + SessionHandle = handle; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs new file mode 100644 index 00000000..e85892f2 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs @@ -0,0 +1,335 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using Ryujinx.Horizon.Sdk.Sm; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + class ServerSessionManager + { + public Result AcceptSession(int portHandle, ServiceObjectHolder obj) + { + return AcceptSession(out _, portHandle, obj); + } + + private Result AcceptSession(out ServerSession session, int portHandle, ServiceObjectHolder obj) + { + return AcceptSessionImpl(out session, portHandle, obj); + } + + private Result AcceptSessionImpl(out ServerSession session, int portHandle, ServiceObjectHolder obj) + { + session = null; + + Result result = HorizonStatic.Syscall.AcceptSession(out int sessionHandle, portHandle); + + if (result.IsFailure) + { + return result; + } + + bool succeeded = false; + + try + { + result = RegisterSessionImpl(out session, sessionHandle, obj); + + if (result.IsFailure) + { + return result; + } + + succeeded = true; + } + finally + { + if (!succeeded) + { + HorizonStatic.Syscall.CloseHandle(sessionHandle); + } + } + + return Result.Success; + } + + public Result RegisterSession(int sessionHandle, ServiceObjectHolder obj) + { + return RegisterSession(out _, sessionHandle, obj); + } + + public Result RegisterSession(out ServerSession session, int sessionHandle, ServiceObjectHolder obj) + { + return RegisterSessionImpl(out session, sessionHandle, obj); + } + + private Result RegisterSessionImpl(out ServerSession session, int sessionHandle, ServiceObjectHolder obj) + { + Result result = CreateSessionImpl(out session, sessionHandle, obj); + + if (result.IsFailure) + { + return result; + } + + session.PointerBuffer = GetSessionPointerBuffer(session); + session.SavedMessage = GetSessionSavedMessageBuffer(session); + + RegisterSessionToWaitList(session); + return Result.Success; + } + + protected virtual void RegisterSessionToWaitList(ServerSession session) + { + throw new NotSupportedException(); + } + + private Result CreateSessionImpl(out ServerSession session, int sessionHandle, ServiceObjectHolder obj) + { + session = AllocateSession(sessionHandle, obj); + + if (session == null) + { + return HipcResult.OutOfSessionMemory; + } + + return Result.Success; + } + + protected virtual ServerSession AllocateSession(int sessionHandle, ServiceObjectHolder obj) + { + throw new NotSupportedException(); + } + + protected virtual void FreeSession(ServerSession session) + { + throw new NotSupportedException(); + } + + protected virtual Server AllocateServer( + int portIndex, + int portHandle, + ServiceName name, + bool managed, + ServiceObjectHolder staticHoder) + { + throw new NotSupportedException(); + } + + protected virtual void DestroyServer(Server server) + { + throw new NotSupportedException(); + } + + protected virtual PointerAndSize GetSessionPointerBuffer(ServerSession session) + { + throw new NotSupportedException(); + } + + protected virtual PointerAndSize GetSessionSavedMessageBuffer(ServerSession session) + { + throw new NotSupportedException(); + } + + private void DestroySession(ServerSession session) + { + FreeSession(session); + } + + protected void CloseSessionImpl(ServerSession session) + { + int sessionHandle = session.Handle; + Os.FinalizeMultiWaitHolder(session); + DestroySession(session); + HorizonStatic.Syscall.CloseHandle(sessionHandle).AbortOnFailure(); + } + + private static CommandType GetCmifCommandType(ReadOnlySpan<byte> message) + { + return MemoryMarshal.Cast<byte, Header>(message)[0].Type; + } + + public Result ProcessRequest(ServerSession session, Span<byte> message) + { + if (session.IsClosed || GetCmifCommandType(message) == CommandType.Close) + { + CloseSessionImpl(session); + return Result.Success; + } + else + { + Result result = ProcessRequestImpl(session, message, message); + + if (result.IsSuccess) + { + RegisterSessionToWaitList(session); + return Result.Success; + } + else if (SfResult.RequestContextChanged(result)) + { + return result; + } + else + { + Logger.Warning?.Print(LogClass.KernelIpc, $"Request processing returned error {result}"); + + CloseSessionImpl(session); + return Result.Success; + } + } + } + + private Result ProcessRequestImpl(ServerSession session, Span<byte> inMessage, Span<byte> outMessage) + { + CommandType commandType = GetCmifCommandType(inMessage); + + using var _ = new ScopedInlineContextChange(GetInlineContext(commandType, inMessage)); + + switch (commandType) + { + case CommandType.Request: + case CommandType.RequestWithContext: + return DispatchRequest(session.ServiceObjectHolder, session, inMessage, outMessage); + case CommandType.Control: + case CommandType.ControlWithContext: + return DispatchManagerRequest(session, inMessage, outMessage); + default: + return HipcResult.UnknownCommandType; + } + } + + private static int GetInlineContext(CommandType commandType, ReadOnlySpan<byte> inMessage) + { + switch (commandType) + { + case CommandType.RequestWithContext: + case CommandType.ControlWithContext: + if (inMessage.Length >= 0x10) + { + return MemoryMarshal.Cast<byte, int>(inMessage)[3]; + } + break; + } + + return 0; + } + + protected Result ReceiveRequest(ServerSession session, Span<byte> message) + { + return ReceiveRequestImpl(session, message); + } + + private Result ReceiveRequestImpl(ServerSession session, Span<byte> message) + { + PointerAndSize pointerBuffer = session.PointerBuffer; + + while (true) + { + if (pointerBuffer.Address != 0) + { + HipcMessageData messageData = HipcMessage.WriteMessage(message, new HipcMetadata() + { + Type = (int)CommandType.Invalid, + ReceiveStaticsCount = HipcMessage.AutoReceiveStatic + }); + + messageData.ReceiveList[0] = new HipcReceiveListEntry(pointerBuffer.Address, pointerBuffer.Size); + } + else + { + MemoryMarshal.Cast<byte, Header>(message)[0] = new Header() + { + Type = CommandType.Invalid + }; + } + + Result result = Api.Receive(out ReceiveResult recvResult, session.Handle, message); + + if (result.IsFailure) + { + return result; + } + + switch (recvResult) + { + case ReceiveResult.Success: + session.IsClosed = false; + return Result.Success; + case ReceiveResult.Closed: + session.IsClosed = true; + return Result.Success; + } + } + } + + protected virtual Result DispatchManagerRequest(ServerSession session, Span<byte> inMessage, Span<byte> outMessage) + { + return SfResult.NotSupported; + } + + protected virtual Result DispatchRequest( + ServiceObjectHolder objectHolder, + ServerSession session, + Span<byte> inMessage, + Span<byte> outMessage) + { + HipcMessage request; + + try + { + request = new HipcMessage(inMessage); + } + catch (ArgumentOutOfRangeException) + { + return HipcResult.InvalidRequestSize; + } + + var dispatchCtx = new ServiceDispatchContext() + { + ServiceObject = objectHolder.ServiceObject, + Manager = this, + Session = session, + HandlesToClose = new HandlesToClose(), + PointerBuffer = session.PointerBuffer, + InMessageBuffer = inMessage, + OutMessageBuffer = outMessage, + Request = request + }; + + ReadOnlySpan<byte> inRawData = MemoryMarshal.Cast<uint, byte>(dispatchCtx.Request.Data.DataWords); + + int inRawSize = dispatchCtx.Request.Meta.DataWordsCount * sizeof(uint); + + if (inRawSize < 0x10) + { + return HipcResult.InvalidRequestSize; + } + + Result result = objectHolder.ProcessMessage(ref dispatchCtx, inRawData); + + if (result.IsFailure) + { + return result; + } + + result = Api.Reply(session.SessionHandle, outMessage); + + ref var handlesToClose = ref dispatchCtx.HandlesToClose; + + for (int i = 0; i < handlesToClose.Count; i++) + { + HorizonStatic.Syscall.CloseHandle(handlesToClose[i]).AbortOnFailure(); + } + + return result; + } + + public ServerSessionManager GetSessionManagerByTag(uint tag) + { + // Official FW does not do anything with the tag currently. + return this; + } + } +} diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs new file mode 100644 index 00000000..8b747626 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs @@ -0,0 +1,27 @@ +using Ryujinx.Common.Utilities; + +namespace Ryujinx.Horizon.Sdk.Sf.Hipc +{ + struct SpecialHeader + { + private uint _word; + + public bool SendPid + { + get => _word.Extract(0); + set => _word = _word.Insert(0, value); + } + + public int CopyHandlesCount + { + get => (int)_word.Extract(1, 4); + set => _word = _word.Insert(1, 4, (uint)value); + } + + public int MoveHandlesCount + { + get => (int)_word.Extract(5, 4); + set => _word = _word.Insert(5, 4, (uint)value); + } + } +} |