using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Sdk.Sf.Cmif; using Ryujinx.Horizon.Sdk.Sf.Hipc; using System; namespace Ryujinx.Horizon.Sdk { static class ServiceUtil { public static Result SendRequest(out CmifResponse response, int sessionHandle, uint requestId, bool sendPid, scoped ReadOnlySpan data) { ulong tlsAddress = HorizonStatic.ThreadContext.TlsAddress; int tlsSize = Api.TlsMessageBufferSize; using (var tlsRegion = HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize)) { CmifRequest request = CmifMessage.CreateRequest(tlsRegion.Memory.Span, new CmifRequestFormat { DataSize = data.Length, RequestId = requestId, SendPid = sendPid, }); 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); } public static Result SendRequest( out CmifResponse response, int sessionHandle, uint requestId, bool sendPid, scoped ReadOnlySpan data, ReadOnlySpan bufferFlags, ReadOnlySpan 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; } } }