diff options
Diffstat (limited to 'src/Ryujinx.Horizon/Sdk/ServiceUtil.cs')
-rw-r--r-- | src/Ryujinx.Horizon/Sdk/ServiceUtil.cs | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/src/Ryujinx.Horizon/Sdk/ServiceUtil.cs b/src/Ryujinx.Horizon/Sdk/ServiceUtil.cs index ccd6c93a..5527c1e3 100644 --- a/src/Ryujinx.Horizon/Sdk/ServiceUtil.cs +++ b/src/Ryujinx.Horizon/Sdk/ServiceUtil.cs @@ -35,5 +35,254 @@ namespace Ryujinx.Horizon.Sdk return CmifMessage.ParseResponse(out response, HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize).Memory.Span, false, 0); } + + public static Result SendRequest( + out CmifResponse response, + int sessionHandle, + uint requestId, + bool sendPid, + scoped ReadOnlySpan<byte> data, + ReadOnlySpan<HipcBufferFlags> bufferFlags, + ReadOnlySpan<PointerAndSize> buffers) + { + ulong tlsAddress = HorizonStatic.ThreadContext.TlsAddress; + int tlsSize = Api.TlsMessageBufferSize; + + using (var tlsRegion = HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize)) + { + CmifRequestFormat format = new() + { + DataSize = data.Length, + RequestId = requestId, + SendPid = sendPid, + }; + + for (int index = 0; index < bufferFlags.Length; index++) + { + FormatProcessBuffer(ref format, bufferFlags[index]); + } + + CmifRequest request = CmifMessage.CreateRequest(tlsRegion.Memory.Span, format); + + for (int index = 0; index < buffers.Length; index++) + { + RequestProcessBuffer(ref request, buffers[index], bufferFlags[index]); + } + + data.CopyTo(request.Data); + } + + Result result = HorizonStatic.Syscall.SendSyncRequest(sessionHandle); + + if (result.IsFailure) + { + response = default; + + return result; + } + + return CmifMessage.ParseResponse(out response, HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize).Memory.Span, false, 0); + } + + private static void FormatProcessBuffer(ref CmifRequestFormat format, HipcBufferFlags flags) + { + if (flags == 0) + { + return; + } + + bool isIn = flags.HasFlag(HipcBufferFlags.In); + bool isOut = flags.HasFlag(HipcBufferFlags.Out); + + if (flags.HasFlag(HipcBufferFlags.AutoSelect)) + { + if (isIn) + { + format.InAutoBuffersCount++; + } + + if (isOut) + { + format.OutAutoBuffersCount++; + } + } + else if (flags.HasFlag(HipcBufferFlags.Pointer)) + { + if (isIn) + { + format.InPointersCount++; + } + + if (isOut) + { + if (flags.HasFlag(HipcBufferFlags.FixedSize)) + { + format.OutFixedPointersCount++; + } + else + { + format.OutPointersCount++; + } + } + } + else if (flags.HasFlag(HipcBufferFlags.MapAlias)) + { + if (isIn && isOut) + { + format.InOutBuffersCount++; + } + else if (isIn) + { + format.InBuffersCount++; + } + else + { + format.OutBuffersCount++; + } + } + } + + private static void RequestProcessBuffer(ref CmifRequest request, PointerAndSize buffer, HipcBufferFlags flags) + { + if (flags == 0) + { + return; + } + + bool isIn = flags.HasFlag(HipcBufferFlags.In); + bool isOut = flags.HasFlag(HipcBufferFlags.Out); + + if (flags.HasFlag(HipcBufferFlags.AutoSelect)) + { + HipcBufferMode mode = HipcBufferMode.Normal; + + if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonSecure)) + { + mode = HipcBufferMode.NonSecure; + } + + if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonDevice)) + { + mode = HipcBufferMode.NonDevice; + } + + if (isIn) + { + RequestInAutoBuffer(ref request, buffer.Address, buffer.Size, mode); + } + + if (isOut) + { + RequestOutAutoBuffer(ref request, buffer.Address, buffer.Size, mode); + } + } + else if (flags.HasFlag(HipcBufferFlags.Pointer)) + { + if (isIn) + { + RequestInPointer(ref request, buffer.Address, buffer.Size); + } + + if (isOut) + { + if (flags.HasFlag(HipcBufferFlags.FixedSize)) + { + RequestOutFixedPointer(ref request, buffer.Address, buffer.Size); + } + else + { + RequestOutPointer(ref request, buffer.Address, buffer.Size); + } + } + } + else if (flags.HasFlag(HipcBufferFlags.MapAlias)) + { + HipcBufferMode mode = HipcBufferMode.Normal; + + if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonSecure)) + { + mode = HipcBufferMode.NonSecure; + } + + if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonDevice)) + { + mode = HipcBufferMode.NonDevice; + } + + if (isIn && isOut) + { + RequestInOutBuffer(ref request, buffer.Address, buffer.Size, mode); + } + else if (isIn) + { + RequestInBuffer(ref request, buffer.Address, buffer.Size, mode); + } + else + { + RequestOutBuffer(ref request, buffer.Address, buffer.Size, mode); + } + } + } + + private static void RequestInAutoBuffer(ref CmifRequest request, ulong bufferAddress, ulong bufferSize, HipcBufferMode mode) + { + if (request.ServerPointerSize != 0 && bufferSize <= (ulong)request.ServerPointerSize) + { + RequestInPointer(ref request, bufferAddress, bufferSize); + RequestInBuffer(ref request, 0UL, 0UL, mode); + } + else + { + RequestInPointer(ref request, 0UL, 0UL); + RequestInBuffer(ref request, bufferAddress, bufferSize, mode); + } + } + + private static void RequestOutAutoBuffer(ref CmifRequest request, ulong bufferAddress, ulong bufferSize, HipcBufferMode mode) + { + if (request.ServerPointerSize != 0 && bufferSize <= (ulong)request.ServerPointerSize) + { + RequestOutPointer(ref request, bufferAddress, bufferSize); + RequestOutBuffer(ref request, 0UL, 0UL, mode); + } + else + { + RequestOutPointer(ref request, 0UL, 0UL); + RequestOutBuffer(ref request, bufferAddress, bufferSize, mode); + } + } + + private static void RequestInBuffer(ref CmifRequest request, ulong bufferAddress, ulong bufferSize, HipcBufferMode mode) + { + request.Hipc.SendBuffers[request.SendBufferIndex++] = new HipcBufferDescriptor(bufferAddress, bufferSize, mode); + } + + private static void RequestOutBuffer(ref CmifRequest request, ulong bufferAddress, ulong bufferSize, HipcBufferMode mode) + { + request.Hipc.ReceiveBuffers[request.RecvBufferIndex++] = new HipcBufferDescriptor(bufferAddress, bufferSize, mode); + } + + private static void RequestInOutBuffer(ref CmifRequest request, ulong bufferAddress, ulong bufferSize, HipcBufferMode mode) + { + request.Hipc.ExchangeBuffers[request.ExchBufferIndex++] = new HipcBufferDescriptor(bufferAddress, bufferSize, mode); + } + + private static void RequestInPointer(ref CmifRequest request, ulong bufferAddress, ulong bufferSize) + { + request.Hipc.SendStatics[request.SendStaticIndex++] = new HipcStaticDescriptor(bufferAddress, (ushort)bufferSize, request.CurrentInPointerId++); + request.ServerPointerSize -= (int)bufferSize; + } + + private static void RequestOutFixedPointer(ref CmifRequest request, ulong bufferAddress, ulong bufferSize) + { + request.Hipc.ReceiveList[request.RecvListIndex++] = new HipcReceiveListEntry(bufferAddress, (ushort)bufferSize); + request.ServerPointerSize -= (int)bufferSize; + } + + private static void RequestOutPointer(ref CmifRequest request, ulong bufferAddress, ulong bufferSize) + { + RequestOutFixedPointer(ref request, bufferAddress, bufferSize); + request.OutPointerSizes[request.OutPointerSizeIndex++] = (ushort)bufferSize; + } } } |