diff options
Diffstat (limited to 'Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs')
-rw-r--r-- | Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs b/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs new file mode 100644 index 00000000..53202ede --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs @@ -0,0 +1,421 @@ +using Ryujinx.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Cmif; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Sf +{ + class HipcCommandProcessor : ServerMessageProcessor + { + private readonly CommandArg[] _args; + + private readonly int[] _inOffsets; + private readonly int[] _outOffsets; + private readonly PointerAndSize[] _bufferRanges; + + private readonly bool _hasInProcessIdHolder; + private readonly int _inObjectsCount; + private readonly int _outObjectsCount; + private readonly int _inMapAliasBuffersCount; + private readonly int _outMapAliasBuffersCount; + private readonly int _inPointerBuffersCount; + private readonly int _outPointerBuffersCount; + private readonly int _outFixedSizePointerBuffersCount; + private readonly int _inMoveHandlesCount; + private readonly int _inCopyHandlesCount; + private readonly int _outMoveHandlesCount; + private readonly int _outCopyHandlesCount; + + public int FunctionArgumentsCount => _args.Length; + + public int InRawDataSize => BitUtils.AlignUp(_inOffsets[^1], sizeof(ushort)); + public int OutRawDataSize => BitUtils.AlignUp(_outOffsets[^1], sizeof(uint)); + + private int OutUnfixedSizePointerBuffersCount => _outPointerBuffersCount - _outFixedSizePointerBuffersCount; + + public HipcCommandProcessor(CommandArg[] args) + { + _args = args; + + for (int i = 0; i < args.Length; i++) + { + var argInfo = args[i]; + + switch (argInfo.Type) + { + case CommandArgType.Buffer: + var flags = argInfo.BufferFlags; + + if (flags.HasFlag(HipcBufferFlags.In)) + { + if (flags.HasFlag(HipcBufferFlags.AutoSelect)) + { + _inMapAliasBuffersCount++; + _inPointerBuffersCount++; + } + else if (flags.HasFlag(HipcBufferFlags.MapAlias)) + { + _inMapAliasBuffersCount++; + } + else if (flags.HasFlag(HipcBufferFlags.Pointer)) + { + _inPointerBuffersCount++; + } + } + else + { + bool autoSelect = flags.HasFlag(HipcBufferFlags.AutoSelect); + if (autoSelect || flags.HasFlag(HipcBufferFlags.Pointer)) + { + _outPointerBuffersCount++; + + if (flags.HasFlag(HipcBufferFlags.FixedSize)) + { + _outFixedSizePointerBuffersCount++; + } + } + + if (autoSelect || flags.HasFlag(HipcBufferFlags.MapAlias)) + { + _outMapAliasBuffersCount++; + } + } + break; + case CommandArgType.InCopyHandle: + _inCopyHandlesCount++; + break; + case CommandArgType.InMoveHandle: + _inMoveHandlesCount++; + break; + case CommandArgType.InObject: + _inObjectsCount++; + break; + case CommandArgType.ProcessId: + _hasInProcessIdHolder = true; + break; + case CommandArgType.OutCopyHandle: + _outCopyHandlesCount++; + break; + case CommandArgType.OutMoveHandle: + _outMoveHandlesCount++; + break; + case CommandArgType.OutObject: + _outObjectsCount++; + break; + } + } + + _inOffsets = RawDataOffsetCalculator.Calculate(args.Where(x => x.Type == CommandArgType.InArgument).ToArray()); + _outOffsets = RawDataOffsetCalculator.Calculate(args.Where(x => x.Type == CommandArgType.OutArgument).ToArray()); + _bufferRanges = new PointerAndSize[args.Length]; + } + + public int GetInArgOffset(int argIndex) + { + return _inOffsets[argIndex]; + } + + public int GetOutArgOffset(int argIndex) + { + return _outOffsets[argIndex]; + } + + public PointerAndSize GetBufferRange(int argIndex) + { + return _bufferRanges[argIndex]; + } + + public Result ProcessBuffers(ref ServiceDispatchContext context, bool[] isBufferMapAlias, ServerMessageRuntimeMetadata runtimeMetadata) + { + bool mapAliasBuffersValid = true; + + ulong pointerBufferTail = context.PointerBuffer.Address; + ulong pointerBufferHead = pointerBufferTail + context.PointerBuffer.Size; + + int sendMapAliasIndex = 0; + int recvMapAliasIndex = 0; + int sendPointerIndex = 0; + int unfixedRecvPointerIndex = 0; + + for (int i = 0; i < _args.Length; i++) + { + if (_args[i].Type != CommandArgType.Buffer) + { + continue; + } + + var flags = _args[i].BufferFlags; + bool isMapAlias; + + if (flags.HasFlag(HipcBufferFlags.MapAlias)) + { + isMapAlias = true; + } + else if (flags.HasFlag(HipcBufferFlags.Pointer)) + { + isMapAlias = false; + } + else /* if (flags.HasFlag(HipcBufferFlags.HipcAutoSelect)) */ + { + var descriptor = flags.HasFlag(HipcBufferFlags.In) + ? context.Request.Data.SendBuffers[sendMapAliasIndex] + : context.Request.Data.ReceiveBuffers[recvMapAliasIndex]; + + isMapAlias = descriptor.Address != 0UL; + } + + isBufferMapAlias[i] = isMapAlias; + + if (isMapAlias) + { + var descriptor = flags.HasFlag(HipcBufferFlags.In) + ? context.Request.Data.SendBuffers[sendMapAliasIndex++] + : context.Request.Data.ReceiveBuffers[recvMapAliasIndex++]; + + _bufferRanges[i] = new PointerAndSize(descriptor.Address, descriptor.Size); + + if (!IsMapTransferModeValid(flags, descriptor.Mode)) + { + mapAliasBuffersValid = false; + } + } + else + { + if (flags.HasFlag(HipcBufferFlags.In)) + { + var descriptor = context.Request.Data.SendStatics[sendPointerIndex++]; + ulong address = descriptor.Address; + ulong size = descriptor.Size; + _bufferRanges[i] = new PointerAndSize(address, size); + + if (size != 0) + { + pointerBufferTail = Math.Max(pointerBufferTail, address + size); + } + } + else /* if (flags.HasFlag(HipcBufferFlags.Out)) */ + { + ulong size; + + if (flags.HasFlag(HipcBufferFlags.FixedSize)) + { + size = _args[i].BufferFixedSize; + } + else + { + var data = MemoryMarshal.Cast<uint, byte>(context.Request.Data.DataWords); + var recvPointerSizes = MemoryMarshal.Cast<byte, ushort>(data.Slice(runtimeMetadata.UnfixedOutPointerSizeOffset)); + size = recvPointerSizes[unfixedRecvPointerIndex++]; + } + + pointerBufferHead = BitUtils.AlignDown(pointerBufferHead - size, 0x10UL); + _bufferRanges[i] = new PointerAndSize(pointerBufferHead, size); + } + } + } + + if (!mapAliasBuffersValid) + { + return HipcResult.InvalidCmifRequest; + } + + if (_outPointerBuffersCount != 0 && pointerBufferTail > pointerBufferHead) + { + return HipcResult.PointerBufferTooSmall; + } + + return Result.Success; + } + + private static bool IsMapTransferModeValid(HipcBufferFlags flags, HipcBufferMode mode) + { + if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonSecure)) + { + return mode == HipcBufferMode.NonSecure; + } + else if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonDevice)) + { + return mode == HipcBufferMode.NonDevice; + } + else + { + return mode == HipcBufferMode.Normal; + } + } + + public void SetOutBuffers(HipcMessageData response, bool[] isBufferMapAlias) + { + int recvPointerIndex = 0; + + for (int i = 0; i < _args.Length; i++) + { + if (_args[i].Type != CommandArgType.Buffer) + { + continue; + } + + var flags = _args[i].BufferFlags; + if (flags.HasFlag(HipcBufferFlags.Out)) + { + var buffer = _bufferRanges[i]; + + if (flags.HasFlag(HipcBufferFlags.Pointer)) + { + response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex); + } + else if (flags.HasFlag(HipcBufferFlags.AutoSelect)) + { + if (!isBufferMapAlias[i]) + { + response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex); + } + else + { + response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(0UL, 0, recvPointerIndex); + } + } + + recvPointerIndex++; + } + } + } + + public override void SetImplementationProcessor(ServerMessageProcessor impl) + { + // We don't need to do anything here as this should be always the last processor to be called. + } + + public override ServerMessageRuntimeMetadata GetRuntimeMetadata() + { + return new ServerMessageRuntimeMetadata( + (ushort)InRawDataSize, + (ushort)OutRawDataSize, + (byte)Unsafe.SizeOf<CmifInHeader>(), + (byte)Unsafe.SizeOf<CmifOutHeader>(), + (byte)_inObjectsCount, + (byte)_outObjectsCount); + } + + public override Result PrepareForProcess(ref ServiceDispatchContext context, ServerMessageRuntimeMetadata runtimeMetadata) + { + ref var meta = ref context.Request.Meta; + bool requestValid = true; + requestValid &= meta.SendPid == _hasInProcessIdHolder; + requestValid &= meta.SendStaticsCount == _inPointerBuffersCount; + requestValid &= meta.SendBuffersCount == _inMapAliasBuffersCount; + requestValid &= meta.ReceiveBuffersCount == _outMapAliasBuffersCount; + requestValid &= meta.ExchangeBuffersCount == 0; + requestValid &= meta.CopyHandlesCount == _inCopyHandlesCount; + requestValid &= meta.MoveHandlesCount == _inMoveHandlesCount; + + int rawSizeInBytes = meta.DataWordsCount * sizeof(uint); + int commandRawSize = BitUtils.AlignUp(runtimeMetadata.UnfixedOutPointerSizeOffset + (OutUnfixedSizePointerBuffersCount * sizeof(ushort)), sizeof(uint)); + requestValid &= rawSizeInBytes >= commandRawSize; + + return requestValid ? Result.Success : HipcResult.InvalidCmifRequest; + } + + public Result GetInObjects(ServerMessageProcessor processor, Span<IServiceObject> objects) + { + if (objects.Length == 0) + { + return Result.Success; + } + + ServiceObjectHolder[] inObjects = new ServiceObjectHolder[objects.Length]; + Result result = processor.GetInObjects(inObjects); + + if (result.IsFailure) + { + return result; + } + + int inObjectIndex = 0; + + for (int i = 0; i < _args.Length; i++) + { + if (_args[i].Type == CommandArgType.InObject) + { + int index = inObjectIndex++; + var inObject = inObjects[index]; + + objects[index] = inObject?.ServiceObject; + } + } + + return Result.Success; + } + + public override Result GetInObjects(Span<ServiceObjectHolder> inObjects) + { + return SfResult.NotSupported; + } + + public override HipcMessageData PrepareForReply(scoped ref ServiceDispatchContext context, out Span<byte> outRawData, ServerMessageRuntimeMetadata runtimeMetadata) + { + int rawDataSize = OutRawDataSize + runtimeMetadata.OutHeadersSize; + var response = HipcMessage.WriteResponse( + context.OutMessageBuffer, + _outPointerBuffersCount, + (BitUtils.AlignUp(rawDataSize, 4) + 0x10) / sizeof(uint), + _outCopyHandlesCount, + _outMoveHandlesCount + runtimeMetadata.OutObjectsCount); + outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords); + return response; + } + + public override void PrepareForErrorReply(scoped ref ServiceDispatchContext context, out Span<byte> outRawData, ServerMessageRuntimeMetadata runtimeMetadata) + { + int rawDataSize = runtimeMetadata.OutHeadersSize; + var response = HipcMessage.WriteResponse( + context.OutMessageBuffer, + 0, + (BitUtils.AlignUp(rawDataSize, 4) + 0x10) / sizeof(uint), + 0, + 0); + outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords); + } + + public void SetOutObjects(ref ServiceDispatchContext context, HipcMessageData response, Span<IServiceObject> objects) + { + if (objects.Length == 0) + { + return; + } + + ServiceObjectHolder[] outObjects = new ServiceObjectHolder[objects.Length]; + + for (int i = 0; i < objects.Length; i++) + { + outObjects[i] = objects[i] != null ? new ServiceObjectHolder(objects[i]) : null; + } + + context.Processor.SetOutObjects(ref context, response, outObjects); + } + + public override void SetOutObjects(scoped ref ServiceDispatchContext context, HipcMessageData response, Span<ServiceObjectHolder> outObjects) + { + for (int index = 0; index < _outObjectsCount; index++) + { + SetOutObjectImpl(index, response, context.Manager, outObjects[index]); + } + } + + private void SetOutObjectImpl(int index, HipcMessageData response, ServerSessionManager manager, ServiceObjectHolder obj) + { + if (obj == null) + { + response.MoveHandles[index] = 0; + return; + } + + Api.CreateSession(out int serverHandle, out int clientHandle).AbortOnFailure(); + manager.RegisterSession(serverHandle, obj).AbortOnFailure(); + response.MoveHandles[index] = clientHandle; + } + } +} |