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
This commit is contained in:
parent
c6a139a6e7
commit
08831eecf7
213 changed files with 9762 additions and 1010 deletions
89
Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs
Normal file
89
Ryujinx.Horizon/Sdk/Sf/Hipc/Api.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
65
Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs
Normal file
65
Ryujinx.Horizon/Sdk/Sf/Hipc/Header.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
15
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs
Normal file
15
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferDescriptor.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
17
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs
Normal file
17
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs
Normal file
|
@ -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
|
||||
}
|
||||
}
|
10
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs
Normal file
10
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||
{
|
||||
enum HipcBufferMode
|
||||
{
|
||||
Normal = 0,
|
||||
NonSecure = 1,
|
||||
Invalid = 2,
|
||||
NonDevice = 3
|
||||
}
|
||||
}
|
115
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs
Normal file
115
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
222
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs
Normal file
222
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs
Normal file
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs
Normal file
16
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
16
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs
Normal file
16
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
14
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs
Normal file
14
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
22
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs
Normal file
22
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
22
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs
Normal file
22
Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs
Normal file
20
Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
9
Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs
Normal file
9
Ryujinx.Horizon/Sdk/Sf/Hipc/ReceiveResult.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||
{
|
||||
enum ReceiveResult
|
||||
{
|
||||
Success,
|
||||
Closed,
|
||||
NeedsRetry
|
||||
}
|
||||
}
|
36
Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs
Normal file
36
Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs
Normal file
23
Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
198
Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs
Normal file
198
Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
307
Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs
Normal file
307
Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
23
Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs
Normal file
23
Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
335
Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs
Normal file
335
Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
27
Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs
Normal file
27
Ryujinx.Horizon/Sdk/Sf/Hipc/SpecialHeader.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue