diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index a8b5be33d..ca3f8103e 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -123,6 +123,8 @@ namespace Ryujinx.HLE.HOS internal LibHacHorizonManager LibHacHorizonManager { get; private set; } + internal ServiceTable ServiceTable { get; private set; } + public bool IsPaused { get; private set; } public Horizon(Switch device) @@ -326,6 +328,7 @@ namespace Ryujinx.HLE.HOS private void StartNewServices() { + ServiceTable = new ServiceTable(); var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices)); foreach (var service in services) diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index 09e1ac31c..b6a39a20a 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -182,6 +182,8 @@ namespace Ryujinx.HLE.HOS byte[] arguments = null, params IExecutable[] executables) { + context.Device.System.ServiceTable.WaitServicesReady(); + LibHac.Result rc = metaData.GetNpdm(out var npdm); if (rc.IsFailure()) diff --git a/Ryujinx.HLE/HOS/Services/Lm/ILogService.cs b/Ryujinx.HLE/HOS/Services/Lm/ILogService.cs deleted file mode 100644 index 0976431b2..000000000 --- a/Ryujinx.HLE/HOS/Services/Lm/ILogService.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Ryujinx.HLE.HOS.Services.Lm.LogService; - -namespace Ryujinx.HLE.HOS.Services.Lm -{ - [Service("lm")] - class ILogService : IpcService - { - public ILogService(ServiceCtx context) { } - - [CommandHipc(0)] - // Initialize(u64, pid) -> object - public ResultCode Initialize(ServiceCtx context) - { - MakeObject(context, new ILogger()); - - return ResultCode.Success; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs b/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs deleted file mode 100644 index 3181668f7..000000000 --- a/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs +++ /dev/null @@ -1,109 +0,0 @@ -using Ryujinx.Common.Logging; -using System.IO; -using System.Text; - -namespace Ryujinx.HLE.HOS.Services.Lm.LogService -{ - class ILogger : IpcService - { - public ILogger() { } - - [CommandHipc(0)] - // Log(buffer) - public ResultCode Log(ServiceCtx context) - { - Logger.Guest?.Print(LogClass.ServiceLm, LogImpl(context)); - - return ResultCode.Success; - } - - private string LogImpl(ServiceCtx context) - { - (ulong bufPos, ulong bufSize) = context.Request.GetBufferType0x21(); - - byte[] logBuffer = new byte[bufSize]; - - context.Memory.Read(bufPos, logBuffer); - - using MemoryStream ms = new MemoryStream(logBuffer); - - BinaryReader reader = new BinaryReader(ms); - - long pid = reader.ReadInt64(); - long threadContext = reader.ReadInt64(); - short flags = reader.ReadInt16(); - byte level = reader.ReadByte(); - byte verbosity = reader.ReadByte(); - int payloadLength = reader.ReadInt32(); - - StringBuilder sb = new StringBuilder(); - - sb.AppendLine($"Guest Log:\n Log level: {(LmLogLevel)level}"); - - while (ms.Position < ms.Length) - { - int type = ReadEncodedInt(reader); - int size = ReadEncodedInt(reader); - - LmLogField field = (LmLogField)type; - - string fieldStr = string.Empty; - - if (field == LmLogField.Start) - { - reader.ReadBytes(size); - - continue; - } - else if (field == LmLogField.Stop) - { - break; - } - else if (field == LmLogField.Line) - { - fieldStr = $"{field}: {reader.ReadInt32()}"; - } - else if (field == LmLogField.DropCount) - { - fieldStr = $"{field}: {reader.ReadInt64()}"; - } - else if (field == LmLogField.Time) - { - fieldStr = $"{field}: {reader.ReadInt64()}s"; - } - else if (field < LmLogField.Count) - { - fieldStr = $"{field}: '{Encoding.UTF8.GetString(reader.ReadBytes(size)).TrimEnd()}'"; - } - else - { - fieldStr = $"Field{field}: '{Encoding.UTF8.GetString(reader.ReadBytes(size)).TrimEnd()}'"; - } - - sb.AppendLine($" {fieldStr}"); - } - - return sb.ToString(); - } - - private static int ReadEncodedInt(BinaryReader reader) - { - int result = 0; - int position = 0; - - byte encoded; - - do - { - encoded = reader.ReadByte(); - - result += (encoded & 0x7F) << (7 * position); - - position++; - - } while ((encoded & 0x80) != 0); - - return result; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogField.cs b/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogField.cs deleted file mode 100644 index 3f93e1676..000000000 --- a/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogField.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Lm.LogService -{ - enum LmLogField - { - Start = 0, - Stop = 1, - Message = 2, - Line = 3, - Filename = 4, - Function = 5, - Module = 6, - Thread = 7, - DropCount = 8, - Time = 9, - ProgramName = 10, - Count - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogLevel.cs b/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogLevel.cs deleted file mode 100644 index ee1a8396a..000000000 --- a/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogLevel.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Lm.LogService -{ - enum LmLogLevel - { - Trace, - Info, - Warning, - Error, - Critical - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs deleted file mode 100644 index 019626f1b..000000000 --- a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs +++ /dev/null @@ -1,182 +0,0 @@ -using MsgPack; -using MsgPack.Serialization; -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; -using Ryujinx.HLE.HOS.Services.Account.Acc; -using Ryujinx.HLE.Utilities; -using System; -using System.Text; - -namespace Ryujinx.HLE.HOS.Services.Prepo -{ - [Service("prepo:a", PrepoServicePermissionLevel.Admin)] // 1.0.0-5.1.0 - [Service("prepo:a2", PrepoServicePermissionLevel.Admin)] // 6.0.0+ - [Service("prepo:m", PrepoServicePermissionLevel.Manager)] - [Service("prepo:u", PrepoServicePermissionLevel.User)] - [Service("prepo:s", PrepoServicePermissionLevel.System)] - class IPrepoService : IpcService - { - private PrepoServicePermissionLevel _permission; - private ulong _systemSessionId; - - public IPrepoService(ServiceCtx context, PrepoServicePermissionLevel permission) - { - _permission = permission; - } - - [CommandHipc(10100)] // 1.0.0-5.1.0 - [CommandHipc(10102)] // 6.0.0-9.2.0 - [CommandHipc(10104)] // 10.0.0+ - // SaveReport(u64, pid, buffer, buffer) - public ResultCode SaveReport(ServiceCtx context) - { - if ((_permission & PrepoServicePermissionLevel.User) == 0) - { - return ResultCode.PermissionDenied; - } - - // We don't care about the differences since we don't use the play report. - return ProcessReport(context, withUserID: false); - } - - [CommandHipc(10101)] // 1.0.0-5.1.0 - [CommandHipc(10103)] // 6.0.0-9.2.0 - [CommandHipc(10105)] // 10.0.0+ - // SaveReportWithUser(nn::account::Uid, u64, pid, buffer, buffer) - public ResultCode SaveReportWithUser(ServiceCtx context) - { - if ((_permission & PrepoServicePermissionLevel.User) == 0) - { - return ResultCode.PermissionDenied; - } - - // We don't care about the differences since we don't use the play report. - return ProcessReport(context, withUserID: true); - } - - [CommandHipc(10200)] - // RequestImmediateTransmission() - public ResultCode RequestImmediateTransmission(ServiceCtx context) - { - // It signals an event of nn::prepo::detail::service::core::TransmissionStatusManager that requests the transmission of the report. - // Since we don't use reports it's fine to do nothing. - - return ResultCode.Success; - } - - [CommandHipc(10300)] - // GetTransmissionStatus() -> u32 - public ResultCode GetTransmissionStatus(ServiceCtx context) - { - // It returns the transmission result of nn::prepo::detail::service::core::TransmissionStatusManager. - // Since we don't use reports it's fine to return ResultCode.Success. - context.ResponseData.Write((int)ResultCode.Success); - - return ResultCode.Success; - } - - [CommandHipc(10400)] // 9.0.0+ - // GetSystemSessionId() -> u64 - public ResultCode GetSystemSessionId(ServiceCtx context) - { - if ((_permission & PrepoServicePermissionLevel.User) == 0) - { - return ResultCode.PermissionDenied; - } - - if (_systemSessionId == 0) - { - byte[] randomBuffer = new byte[8]; - - Random.Shared.NextBytes(randomBuffer); - - _systemSessionId = BitConverter.ToUInt64(randomBuffer, 0); - } - - context.ResponseData.Write(_systemSessionId); - - return ResultCode.Success; - } - - [CommandHipc(20100)] - // SaveSystemReport(u64, pid, buffer, buffer) - public ResultCode SaveSystemReport(ServiceCtx context) - { - if ((_permission & PrepoServicePermissionLevel.System) != 0) - { - return ResultCode.PermissionDenied; - } - - // We don't care about the differences since we don't use the play report. - return ProcessReport(context, withUserID: false); - } - - [CommandHipc(20101)] - // SaveSystemReportWithUser(nn::account::Uid, u64, pid, buffer, buffer) - public ResultCode SaveSystemReportWithUser(ServiceCtx context) - { - if ((_permission & PrepoServicePermissionLevel.System) != 0) - { - return ResultCode.PermissionDenied; - } - - // We don't care about the differences since we don't use the play report. - return ProcessReport(context, withUserID: true); - } - - private ResultCode ProcessReport(ServiceCtx context, bool withUserID) - { - UserId userId = withUserID ? context.RequestData.ReadStruct() : new UserId(); - string gameRoom = StringUtils.ReadUtf8String(context); - - if (withUserID) - { - if (userId.IsNull) - { - return ResultCode.InvalidArgument; - } - } - - if (gameRoom == string.Empty) - { - return ResultCode.InvalidState; - } - - ulong inputPosition = context.Request.SendBuff[0].Position; - ulong inputSize = context.Request.SendBuff[0].Size; - - if (inputSize == 0) - { - return ResultCode.InvalidBufferSize; - } - - byte[] inputBuffer = new byte[inputSize]; - - context.Memory.Read(inputPosition, inputBuffer); - - Logger.Info?.Print(LogClass.ServicePrepo, ReadReportBuffer(inputBuffer, gameRoom, userId)); - - return ResultCode.Success; - } - - private string ReadReportBuffer(byte[] buffer, string room, UserId userId) - { - StringBuilder builder = new StringBuilder(); - MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(buffer); - - builder.AppendLine(); - builder.AppendLine("PlayReport log:"); - - if (!userId.IsNull) - { - builder.AppendLine($" UserId: {userId}"); - } - - builder.AppendLine($" Room: {room}"); - builder.AppendLine($" Report: {MessagePackObjectFormatter.Format(deserializedReport)}"); - - return builder.ToString(); - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs deleted file mode 100644 index 3199e2705..000000000 --- a/Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Prepo -{ - enum ResultCode - { - ModuleId = 129, - ErrorCodeShift = 9, - - Success = 0, - - InvalidArgument = (1 << ErrorCodeShift) | ModuleId, - InvalidState = (5 << ErrorCodeShift) | ModuleId, - InvalidBufferSize = (9 << ErrorCodeShift) | ModuleId, - PermissionDenied = (90 << ErrorCodeShift) | ModuleId - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index 86031a707..73652da4b 100644 --- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -180,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm return ResultCode.InvalidName; } - Logger.Info?.Print(LogClass.ServiceSm, $"Register \"{name}\"."); + Logger.Debug?.Print(LogClass.ServiceSm, $"Register \"{name}\"."); KPort port = new KPort(context.Device.System.KernelContext, maxSessions, isLight, null); diff --git a/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs b/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs index a66d57a3b..332e04d17 100644 --- a/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs +++ b/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs @@ -123,44 +123,51 @@ namespace Ryujinx.Horizon.Generators.Hipc { string[] args = new string[method.ParameterList.Parameters.Count]; - int index = 0; - - foreach (var parameter in method.ParameterList.Parameters) + if (args.Length == 0) { - string canonicalTypeName = GetCanonicalTypeNameWithGenericArguments(compilation, parameter.Type); - CommandArgType argType = GetCommandArgType(compilation, parameter); + generator.AppendLine($"{{ {commandId}, new CommandHandler({method.Identifier.Text}, Array.Empty()) }},"); + } + else + { + int index = 0; - string arg; - - if (argType == CommandArgType.Buffer) + foreach (var parameter in method.ParameterList.Parameters) { - string bufferFlags = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 0); - string bufferFixedSize = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 1); + string canonicalTypeName = GetCanonicalTypeNameWithGenericArguments(compilation, parameter.Type); + CommandArgType argType = GetCommandArgType(compilation, parameter); - if (bufferFixedSize != null) + string arg; + + if (argType == CommandArgType.Buffer) { - arg = $"new CommandArg({bufferFlags}, {bufferFixedSize})"; + string bufferFlags = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 0); + string bufferFixedSize = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 1); + + if (bufferFixedSize != null) + { + arg = $"new CommandArg({bufferFlags}, {bufferFixedSize})"; + } + else + { + arg = $"new CommandArg({bufferFlags})"; + } + } + else if (argType == CommandArgType.InArgument || argType == CommandArgType.OutArgument) + { + string alignment = GetTypeAlignmentExpression(compilation, parameter.Type); + + arg = $"new CommandArg(CommandArgType.{argType}, Unsafe.SizeOf<{canonicalTypeName}>(), {alignment})"; } else { - arg = $"new CommandArg({bufferFlags})"; + arg = $"new CommandArg(CommandArgType.{argType})"; } - } - else if (argType == CommandArgType.InArgument || argType == CommandArgType.OutArgument) - { - string alignment = GetTypeAlignmentExpression(compilation, parameter.Type); - arg = $"new CommandArg(CommandArgType.{argType}, Unsafe.SizeOf<{canonicalTypeName}>(), {alignment})"; - } - else - { - arg = $"new CommandArg(CommandArgType.{argType})"; + args[index++] = arg; } - args[index++] = arg; + generator.AppendLine($"{{ {commandId}, new CommandHandler({method.Identifier.Text}, {string.Join(", ", args)}) }},"); } - - generator.AppendLine($"{{ {commandId}, new CommandHandler({method.Identifier.Text}, {string.Join(", ", args)}) }},"); } } diff --git a/Ryujinx.Horizon/HeapAllocator.cs b/Ryujinx.Horizon/HeapAllocator.cs index 867c96770..40ff14d09 100644 --- a/Ryujinx.Horizon/HeapAllocator.cs +++ b/Ryujinx.Horizon/HeapAllocator.cs @@ -12,12 +12,12 @@ namespace Ryujinx.Horizon private struct Range : IComparable { public ulong Offset { get; } - public ulong Size { get; } + public ulong Size { get; } public Range(ulong offset, ulong size) { Offset = offset; - Size = size; + Size = size; } public int CompareTo(Range other) @@ -31,7 +31,7 @@ namespace Ryujinx.Horizon public HeapAllocator() { - _freeRanges = new List(); + _freeRanges = new List(); _currentHeapSize = 0; } @@ -70,8 +70,8 @@ namespace Ryujinx.Horizon var range = _freeRanges[i]; ulong alignedOffset = BitUtils.AlignUp(range.Offset, alignment); - ulong sizeDelta = alignedOffset - range.Offset; - ulong usableSize = range.Size - sizeDelta; + ulong sizeDelta = alignedOffset - range.Offset; + ulong usableSize = range.Size - sizeDelta; if (sizeDelta < range.Size && usableSize >= size) { @@ -82,7 +82,7 @@ namespace Ryujinx.Horizon InsertFreeRange(range.Offset, sizeDelta); } - ulong endOffset = range.Offset + range.Size; + ulong endOffset = range.Offset + range.Size; ulong remainingSize = endOffset - (alignedOffset + size); if (remainingSize != 0) { diff --git a/Ryujinx.Horizon/HorizonOptions.cs b/Ryujinx.Horizon/HorizonOptions.cs index b1567c6a4..6d580e8b9 100644 --- a/Ryujinx.Horizon/HorizonOptions.cs +++ b/Ryujinx.Horizon/HorizonOptions.cs @@ -2,11 +2,13 @@ namespace Ryujinx.Horizon { public struct HorizonOptions { - public bool IgnoreMissingServices { get; } + public bool IgnoreMissingServices { get; } + public bool ThrowOnInvalidCommandIds { get; } public HorizonOptions(bool ignoreMissingServices) { - IgnoreMissingServices = ignoreMissingServices; + IgnoreMissingServices = ignoreMissingServices; + ThrowOnInvalidCommandIds = true; } } } diff --git a/Ryujinx.Horizon/HorizonStatic.cs b/Ryujinx.Horizon/HorizonStatic.cs index 1e483cd44..e372df699 100644 --- a/Ryujinx.Horizon/HorizonStatic.cs +++ b/Ryujinx.Horizon/HorizonStatic.cs @@ -21,24 +21,24 @@ namespace Ryujinx.Horizon [ThreadStatic] private static int _threadHandle; - public static HorizonOptions Options => _options; - public static ISyscallApi Syscall => _syscall; - public static IVirtualMemoryManager AddressSpace => _addressSpace; - public static IThreadContext ThreadContext => _threadContext; - public static int CurrentThreadHandle => _threadHandle; + public static HorizonOptions Options => _options; + public static ISyscallApi Syscall => _syscall; + public static IVirtualMemoryManager AddressSpace => _addressSpace; + public static IThreadContext ThreadContext => _threadContext; + public static int CurrentThreadHandle => _threadHandle; public static void Register( - HorizonOptions options, - ISyscallApi syscallApi, + HorizonOptions options, + ISyscallApi syscallApi, IVirtualMemoryManager addressSpace, - IThreadContext threadContext, - int threadHandle) + IThreadContext threadContext, + int threadHandle) { - _options = options; - _syscall = syscallApi; - _addressSpace = addressSpace; + _options = options; + _syscall = syscallApi; + _addressSpace = addressSpace; _threadContext = threadContext; - _threadHandle = threadHandle; + _threadHandle = threadHandle; } } } diff --git a/Ryujinx.Horizon/IService.cs b/Ryujinx.Horizon/IService.cs index 67c12cef6..c0bc0bcb7 100644 --- a/Ryujinx.Horizon/IService.cs +++ b/Ryujinx.Horizon/IService.cs @@ -2,6 +2,6 @@ { interface IService { - abstract static void Main(); + abstract static void Main(ServiceTable serviceTable); } } diff --git a/Ryujinx.Horizon/LogManager/LmLogger.cs b/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs similarity index 81% rename from Ryujinx.Horizon/LogManager/LmLogger.cs rename to Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs index 461776cd8..002a59824 100644 --- a/Ryujinx.Horizon/LogManager/LmLogger.cs +++ b/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs @@ -9,23 +9,23 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; -namespace Ryujinx.Horizon.LogManager +namespace Ryujinx.Horizon.LogManager.Ipc { - partial class LmLogger : IServiceObject + partial class LmLogger : ILmLogger { - private readonly LmLog _log; - private readonly ulong _clientProcessId; + private readonly LogService _log; + private readonly ulong _pid; - public LmLogger(LmLog log, ulong clientProcessId) + public LmLogger(LogService log, ulong pid) { _log = log; - _clientProcessId = clientProcessId; + _pid = pid; } [CmifCommand(0)] public Result Log([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] Span message) { - if (!SetProcessId(message, _clientProcessId)) + if (!SetProcessId(message, _pid)) { return Result.Success; } @@ -35,7 +35,7 @@ namespace Ryujinx.Horizon.LogManager return Result.Success; } - [CmifCommand(1)] + [CmifCommand(1)] // 3.0.0+ public Result SetDestination(LogDestination destination) { _log.LogDestination = destination; @@ -48,7 +48,6 @@ namespace Ryujinx.Horizon.LogManager ref LogPacketHeader header = ref MemoryMarshal.Cast(message)[0]; uint expectedMessageSize = (uint)Unsafe.SizeOf() + header.PayloadSize; - if (expectedMessageSize != (uint)message.Length) { Logger.Warning?.Print(LogClass.ServiceLm, $"Invalid message size (expected 0x{expectedMessageSize:X} but got 0x{message.Length:X})."); @@ -63,13 +62,11 @@ namespace Ryujinx.Horizon.LogManager private static string LogImpl(ReadOnlySpan message) { - SpanReader reader = new SpanReader(message); + SpanReader reader = new(message); + LogPacketHeader header = reader.Read(); + StringBuilder builder = new(); - LogPacketHeader header = reader.Read(); - - StringBuilder sb = new StringBuilder(); - - sb.AppendLine($"Guest Log:\n Log level: {header.Severity}"); + builder.AppendLine($"Guest Log:\n Log level: {header.Severity}"); while (reader.Length > 0) { @@ -78,7 +75,7 @@ namespace Ryujinx.Horizon.LogManager LogDataChunkKey field = (LogDataChunkKey)type; - string fieldStr = string.Empty; + string fieldStr; if (field == LogDataChunkKey.Start) { @@ -111,16 +108,16 @@ namespace Ryujinx.Horizon.LogManager fieldStr = $"Field{field}: '{Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd()}'"; } - sb.AppendLine($" {fieldStr}"); + builder.AppendLine($" {fieldStr}"); } - return sb.ToString(); + return builder.ToString(); } private static int ReadUleb128(ref SpanReader reader) { int result = 0; - int count = 0; + int count = 0; byte encoded; @@ -136,4 +133,4 @@ namespace Ryujinx.Horizon.LogManager return result; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/LogManager/LmLog.cs b/Ryujinx.Horizon/LogManager/Ipc/LogService.cs similarity index 55% rename from Ryujinx.Horizon/LogManager/LmLog.cs rename to Ryujinx.Horizon/LogManager/Ipc/LogService.cs index 772465c45..6899739e3 100644 --- a/Ryujinx.Horizon/LogManager/LmLog.cs +++ b/Ryujinx.Horizon/LogManager/Ipc/LogService.cs @@ -2,18 +2,19 @@ using Ryujinx.Horizon.Sdk.Lm; using Ryujinx.Horizon.Sdk.Sf; -namespace Ryujinx.Horizon.LogManager +namespace Ryujinx.Horizon.LogManager.Ipc { - partial class LmLog : IServiceObject + partial class LogService : ILogService { public LogDestination LogDestination { get; set; } = LogDestination.TargetManager; [CmifCommand(0)] - public Result OpenLogger(out LmLogger logger, [ClientProcessId] ulong clientProcessId) + public Result OpenLogger(out LmLogger logger, [ClientProcessId] ulong pid) { - logger = new LmLogger(this, clientProcessId); + // NOTE: Internal name is Logger, but we rename it LmLogger to avoid name clash with Ryujinx.Common.Logging logger. + logger = new LmLogger(this, pid); return Result.Success; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/LogManager/LmIpcServer.cs b/Ryujinx.Horizon/LogManager/LmIpcServer.cs index 7b757fe98..71b844a2b 100644 --- a/Ryujinx.Horizon/LogManager/LmIpcServer.cs +++ b/Ryujinx.Horizon/LogManager/LmIpcServer.cs @@ -1,6 +1,6 @@ -using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.LogManager.Ipc; +using Ryujinx.Horizon.Sdk.Sf.Hipc; using Ryujinx.Horizon.Sdk.Sm; -using Ryujinx.Horizon.Sm; namespace Ryujinx.Horizon.LogManager { @@ -9,36 +9,25 @@ namespace Ryujinx.Horizon.LogManager private const int LogMaxSessionsCount = 42; private const int PointerBufferSize = 0x400; - private const int MaxDomains = 31; - private const int MaxDomainObjects = 61; + private const int MaxDomains = 31; + private const int MaxDomainObjects = 61; + private const int MaxPortsCount = 1; - private const int MaxPortsCount = 1; + private static readonly ManagerOptions _logManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); - private static readonly ManagerOptions _logManagerOptions = new ManagerOptions( - PointerBufferSize, - MaxDomains, - MaxDomainObjects, - false); - - private static readonly ServiceName _logServiceName = ServiceName.Encode("lm"); - - private SmApi _sm; + private SmApi _sm; private ServerManager _serverManager; - private LmLog _logServiceObject; - public void Initialize() { - HeapAllocator allocator = new HeapAllocator(); + HeapAllocator allocator = new(); _sm = new SmApi(); _sm.Initialize().AbortOnFailure(); _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, LogMaxSessionsCount); - _logServiceObject = new LmLog(); - - _serverManager.RegisterObjectForServer(_logServiceObject, _logServiceName, LogMaxSessionsCount); + _serverManager.RegisterObjectForServer(new LogService(), ServiceName.Encode("lm"), LogMaxSessionsCount); } public void ServiceRequests() @@ -51,4 +40,4 @@ namespace Ryujinx.Horizon.LogManager _serverManager.Dispose(); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/LogManager/LmMain.cs b/Ryujinx.Horizon/LogManager/LmMain.cs index 8c0262ac6..bbe96d4c9 100644 --- a/Ryujinx.Horizon/LogManager/LmMain.cs +++ b/Ryujinx.Horizon/LogManager/LmMain.cs @@ -2,13 +2,16 @@ { class LmMain : IService { - public static void Main() + public static void Main(ServiceTable serviceTable) { - LmIpcServer ipcServer = new LmIpcServer(); + LmIpcServer ipcServer = new(); ipcServer.Initialize(); + + serviceTable.SignalServiceReady(); + ipcServer.ServiceRequests(); ipcServer.Shutdown(); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs b/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs new file mode 100644 index 000000000..672bba4ea --- /dev/null +++ b/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs @@ -0,0 +1,218 @@ +using MsgPack; +using MsgPack.Serialization; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Prepo.Types; +using Ryujinx.Horizon.Sdk.Account; +using Ryujinx.Horizon.Sdk.Prepo; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Text; + +namespace Ryujinx.Horizon.Prepo.Ipc +{ + partial class PrepoService : IPrepoService + { + enum PlayReportKind + { + Normal, + System + } + + private readonly PrepoServicePermissionLevel _permissionLevel; + private ulong _systemSessionId; + + private bool _immediateTransmissionEnabled = false; + private bool _userAgreementCheckEnabled = true; + + public PrepoService(PrepoServicePermissionLevel permissionLevel) + { + _permissionLevel = permissionLevel; + } + + [CmifCommand(10100)] // 1.0.0-5.1.0 + [CmifCommand(10102)] // 6.0.0-9.2.0 + [CmifCommand(10104)] // 10.0.0+ + public Result SaveReport([Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan reportBuffer, [ClientProcessId] ulong pid) + { + if ((_permissionLevel & PrepoServicePermissionLevel.User) == 0) + { + return PrepoResult.PermissionDenied; + } + + ProcessPlayReport(PlayReportKind.Normal, pid, gameRoomBuffer, reportBuffer, Uid.Null); + + return Result.Success; + } + + [CmifCommand(10101)] // 1.0.0-5.1.0 + [CmifCommand(10103)] // 6.0.0-9.2.0 + [CmifCommand(10105)] // 10.0.0+ + public Result SaveReportWithUser(Uid userId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan reportBuffer, [ClientProcessId] ulong pid) + { + if ((_permissionLevel & PrepoServicePermissionLevel.User) == 0) + { + return PrepoResult.PermissionDenied; + } + + ProcessPlayReport(PlayReportKind.Normal, pid, gameRoomBuffer, reportBuffer, userId, true); + + return Result.Success; + } + + [CmifCommand(10200)] + public Result RequestImmediateTransmission() + { + _immediateTransmissionEnabled = true; + + // It signals an event of nn::prepo::detail::service::core::TransmissionStatusManager that requests the transmission of the report. + // Since we don't use reports, it's fine to do nothing. + + return Result.Success; + } + + [CmifCommand(10300)] + public Result GetTransmissionStatus(out int status) + { + status = 0; + + if (_immediateTransmissionEnabled && _userAgreementCheckEnabled) + { + status = 1; + } + + return Result.Success; + } + + [CmifCommand(10400)] // 9.0.0+ + public Result GetSystemSessionId(out ulong systemSessionId) + { + systemSessionId = default; + + if ((_permissionLevel & PrepoServicePermissionLevel.User) == 0) + { + return PrepoResult.PermissionDenied; + } + + if (_systemSessionId == 0) + { + _systemSessionId = (ulong)Random.Shared.NextInt64(); + } + + systemSessionId = _systemSessionId; + + return Result.Success; + } + + [CmifCommand(20100)] + public Result SaveSystemReport([Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan reportBuffer, [ClientProcessId] ulong pid) + { + if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0) + { + return PrepoResult.PermissionDenied; + } + + return ProcessPlayReport(PlayReportKind.System, pid, gameRoomBuffer, reportBuffer, Uid.Null); + } + + [CmifCommand(20101)] + public Result SaveSystemReportWithUser(Uid userId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan reportBuffer, [ClientProcessId] ulong pid) + { + if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0) + { + return PrepoResult.PermissionDenied; + } + + return ProcessPlayReport(PlayReportKind.System, pid, gameRoomBuffer, reportBuffer, userId, true); + } + + [CmifCommand(40100)] // 2.0.0+ + public Result IsUserAgreementCheckEnabled(out bool enabled) + { + enabled = false; + + if (_permissionLevel == PrepoServicePermissionLevel.User || _permissionLevel == PrepoServicePermissionLevel.System) + { + enabled = _userAgreementCheckEnabled; + + // If "enabled" is false, it sets some internal fields to 0. + // Then, it mounts "prepo-sys:/is_user_agreement_check_enabled.bin" and returns the contained bool. + // We can return the private bool instead, we don't care about the agreement since we don't send reports. + + return Result.Success; + } + + return PrepoResult.PermissionDenied; + } + + [CmifCommand(40101)] // 2.0.0+ + public Result SetUserAgreementCheckEnabled(bool enabled) + { + if (_permissionLevel == PrepoServicePermissionLevel.User || _permissionLevel == PrepoServicePermissionLevel.System) + { + _userAgreementCheckEnabled = enabled; + + // If "enabled" is false, it sets some internal fields to 0. + // Then, it mounts "prepo-sys:/is_user_agreement_check_enabled.bin" and stores the "enabled" value. + // We can store in the private bool instead, we don't care about the agreement since we don't send reports. + + return Result.Success; + } + + return PrepoResult.PermissionDenied; + } + + private static Result ProcessPlayReport(PlayReportKind playReportKind, ulong pid, ReadOnlySpan gameRoomBuffer, ReadOnlySpan reportBuffer, Uid userId, bool withUserId = false) + { + if (withUserId) + { + if (userId.IsNull) + { + return PrepoResult.InvalidArgument; + } + } + + if (gameRoomBuffer.Length > 31) + { + return PrepoResult.InvalidArgument; + } + + string gameRoom = Encoding.UTF8.GetString(gameRoomBuffer).TrimEnd(); + + if (gameRoom == string.Empty) + { + return PrepoResult.InvalidState; + } + + if (reportBuffer.Length == 0) + { + return PrepoResult.InvalidBufferSize; + } + + // NOTE: The service calls arp:r using the pid to get the application id, if it fails PrepoResult.InvalidPid is returned. + // Reports are stored internally and an event is signaled to transmit them. + + StringBuilder builder = new(); + MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(reportBuffer.ToArray()); + + builder.AppendLine(); + builder.AppendLine("PlayReport log:"); + builder.AppendLine($" Kind: {playReportKind}"); + builder.AppendLine($" Pid: {pid}"); + + if (!userId.IsNull) + { + builder.AppendLine($" UserId: {userId}"); + } + + builder.AppendLine($" Room: {gameRoom}"); + builder.AppendLine($" Report: {MessagePackObjectFormatter.Format(deserializedReport)}"); + + Logger.Info?.Print(LogClass.ServicePrepo, builder.ToString()); + + return Result.Success; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs b/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs new file mode 100644 index 000000000..ba19ff6f6 --- /dev/null +++ b/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs @@ -0,0 +1,49 @@ +using Ryujinx.Horizon.Prepo.Types; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; + +namespace Ryujinx.Horizon.Prepo +{ + class PrepoIpcServer + { + private const int PrepoMaxSessionsCount = 12; + private const int PrepoTotalMaxSessionsCount = PrepoMaxSessionsCount * 6; + + private const int PointerBufferSize = 0x3800; + private const int MaxDomains = 64; + private const int MaxDomainObjects = 16; + private const int MaxPortsCount = 6; + + private static readonly ManagerOptions _logManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); + + private SmApi _sm; + private PrepoServerManager _serverManager; + + public void Initialize() + { + HeapAllocator allocator = new(); + + _sm = new SmApi(); + _sm.Initialize().AbortOnFailure(); + + _serverManager = new PrepoServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, PrepoTotalMaxSessionsCount); + + _serverManager.RegisterServer((int)PrepoPortIndex.Admin, ServiceName.Encode("prepo:a"), PrepoMaxSessionsCount); // 1.0.0-5.1.0 + _serverManager.RegisterServer((int)PrepoPortIndex.Admin2, ServiceName.Encode("prepo:a2"), PrepoMaxSessionsCount); // 6.0.0+ + _serverManager.RegisterServer((int)PrepoPortIndex.Manager, ServiceName.Encode("prepo:m"), PrepoMaxSessionsCount); + _serverManager.RegisterServer((int)PrepoPortIndex.User, ServiceName.Encode("prepo:u"), PrepoMaxSessionsCount); + _serverManager.RegisterServer((int)PrepoPortIndex.System, ServiceName.Encode("prepo:s"), PrepoMaxSessionsCount); + _serverManager.RegisterServer((int)PrepoPortIndex.Debug, ServiceName.Encode("prepo:d"), PrepoMaxSessionsCount); // 1.0.0 + } + + public void ServiceRequests() + { + _serverManager.ServiceRequests(); + } + + public void Shutdown() + { + _serverManager.Dispose(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Prepo/PrepoMain.cs b/Ryujinx.Horizon/Prepo/PrepoMain.cs new file mode 100644 index 000000000..5ff0f53d9 --- /dev/null +++ b/Ryujinx.Horizon/Prepo/PrepoMain.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Horizon.Prepo +{ + class PrepoMain : IService + { + public static void Main(ServiceTable serviceTable) + { + PrepoIpcServer ipcServer = new(); + + ipcServer.Initialize(); + + serviceTable.SignalServiceReady(); + + ipcServer.ServiceRequests(); + ipcServer.Shutdown(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Prepo/PrepoResult.cs b/Ryujinx.Horizon/Prepo/PrepoResult.cs new file mode 100644 index 000000000..12255e3d8 --- /dev/null +++ b/Ryujinx.Horizon/Prepo/PrepoResult.cs @@ -0,0 +1,15 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Prepo +{ + static class PrepoResult + { + private const int ModuleId = 129; + + public static Result InvalidArgument => new(ModuleId, 1); + public static Result InvalidState => new(ModuleId, 5); + public static Result InvalidBufferSize => new(ModuleId, 9); + public static Result PermissionDenied => new(ModuleId, 90); + public static Result InvalidPid => new(ModuleId, 101); + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Prepo/PrepoServerManager.cs b/Ryujinx.Horizon/Prepo/PrepoServerManager.cs new file mode 100644 index 000000000..55e4ff7db --- /dev/null +++ b/Ryujinx.Horizon/Prepo/PrepoServerManager.cs @@ -0,0 +1,30 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Prepo.Ipc; +using Ryujinx.Horizon.Prepo.Types; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; +using System; + +namespace Ryujinx.Horizon.Prepo +{ + class PrepoServerManager : ServerManager + { + public PrepoServerManager(HeapAllocator allocator, SmApi sm, int maxPorts, ManagerOptions options, int maxSessions) : base(allocator, sm, maxPorts, options, maxSessions) + { + } + + protected override Result OnNeedsToAccept(int portIndex, Server server) + { + return (PrepoPortIndex)portIndex switch + { + PrepoPortIndex.Admin => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Admin)), + PrepoPortIndex.Admin2 => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Admin)), + PrepoPortIndex.Manager => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Manager)), + PrepoPortIndex.User => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.User)), + PrepoPortIndex.System => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.System)), + PrepoPortIndex.Debug => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Debug)), + _ => throw new ArgumentOutOfRangeException(nameof(portIndex)), + }; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs b/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs new file mode 100644 index 000000000..f4d6b8773 --- /dev/null +++ b/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Horizon.Prepo.Types +{ + enum PrepoPortIndex + { + Admin, + Admin2, + Manager, + User, + System, + Debug + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Prepo/Types/PrepoServicePermissionLevel.cs b/Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs similarity index 51% rename from Ryujinx.HLE/HOS/Services/Prepo/Types/PrepoServicePermissionLevel.cs rename to Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs index 35774707d..8214f4b9a 100644 --- a/Ryujinx.HLE/HOS/Services/Prepo/Types/PrepoServicePermissionLevel.cs +++ b/Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs @@ -1,10 +1,11 @@ -namespace Ryujinx.HLE.HOS.Services.Prepo +namespace Ryujinx.Horizon.Prepo.Types { enum PrepoServicePermissionLevel { Admin = -1, User = 1, System = 2, - Manager = 6 + Manager = 6, + Debug = unchecked((int)0x80000006) } } \ No newline at end of file diff --git a/Ryujinx.Horizon/Ryujinx.Horizon.csproj b/Ryujinx.Horizon/Ryujinx.Horizon.csproj index e4591c6f9..0139c367f 100644 --- a/Ryujinx.Horizon/Ryujinx.Horizon.csproj +++ b/Ryujinx.Horizon/Ryujinx.Horizon.csproj @@ -11,4 +11,8 @@ + + + + diff --git a/Ryujinx.Horizon/Sdk/Account/Uid.cs b/Ryujinx.Horizon/Sdk/Account/Uid.cs new file mode 100644 index 000000000..a0a399781 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Account/Uid.cs @@ -0,0 +1,62 @@ +using System; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Account +{ + [StructLayout(LayoutKind.Sequential)] + public readonly record struct Uid + { + public readonly long High; + public readonly long Low; + + public bool IsNull => (Low | High) == 0; + + public static Uid Null => new(0, 0); + + public Uid(long low, long high) + { + Low = low; + High = high; + } + + public Uid(byte[] bytes) + { + High = BitConverter.ToInt64(bytes, 0); + Low = BitConverter.ToInt64(bytes, 8); + } + + public Uid(string hex) + { + if (hex == null || hex.Length != 32 || !hex.All("0123456789abcdefABCDEF".Contains)) + { + throw new ArgumentException("Invalid Hex value!", nameof(hex)); + } + + Low = Convert.ToInt64(hex[16..], 16); + High = Convert.ToInt64(hex[..16], 16); + } + + public void Write(BinaryWriter binaryWriter) + { + binaryWriter.Write(High); + binaryWriter.Write(Low); + } + + public override string ToString() + { + return High.ToString("x16") + Low.ToString("x16"); + } + + public LibHac.Account.Uid ToLibHacUid() + { + return new LibHac.Account.Uid((ulong)High, (ulong)Low); + } + + public UInt128 ToUInt128() + { + return new UInt128((ulong)High, (ulong)Low); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs b/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs new file mode 100644 index 000000000..bb5770cbc --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs @@ -0,0 +1,12 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Lm +{ + interface ILmLogger : IServiceObject + { + Result Log(Span message); + Result SetDestination(LogDestination destination); + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Lm/ILogService.cs b/Ryujinx.Horizon/Sdk/Lm/ILogService.cs new file mode 100644 index 000000000..ad6c8455a --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Lm/ILogService.cs @@ -0,0 +1,11 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.LogManager.Ipc; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Lm +{ + interface ILogService : IServiceObject + { + Result OpenLogger(out LmLogger logger, ulong pid); + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs b/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs new file mode 100644 index 000000000..f61ee61bd --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs @@ -0,0 +1,20 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Account; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Prepo +{ + interface IPrepoService : IServiceObject + { + Result SaveReport(ReadOnlySpan gameRoomBuffer, ReadOnlySpan reportBuffer, ulong pid); + Result SaveReportWithUser(Uid userId, ReadOnlySpan gameRoomBuffer, ReadOnlySpan reportBuffer, ulong pid); + Result RequestImmediateTransmission(); + Result GetTransmissionStatus(out int status); + Result GetSystemSessionId(out ulong systemSessionId); + Result SaveSystemReport(ReadOnlySpan gameRoomBuffer, ReadOnlySpan reportBuffer, ulong pid); + Result SaveSystemReportWithUser(Uid userId, ReadOnlySpan gameRoomBuffer, ReadOnlySpan reportBuffer, ulong pid); + Result IsUserAgreementCheckEnabled(out bool enabled); + Result SetUserAgreementCheckEnabled(bool enabled); + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/ServiceUtil.cs b/Ryujinx.Horizon/Sdk/ServiceUtil.cs index 413ac1f6b..fe6fcce15 100644 --- a/Ryujinx.Horizon/Sdk/ServiceUtil.cs +++ b/Ryujinx.Horizon/Sdk/ServiceUtil.cs @@ -10,15 +10,15 @@ namespace Ryujinx.Horizon.Sdk 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; + int tlsSize = Api.TlsMessageBufferSize; using (var tlsRegion = HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize)) { CmifRequest request = CmifMessage.CreateRequest(tlsRegion.Memory.Span, new CmifRequestFormat() { - DataSize = data.Length, + DataSize = data.Length, RequestId = requestId, - SendPid = sendPid + SendPid = sendPid }); data.CopyTo(request.Data); @@ -29,10 +29,11 @@ namespace Ryujinx.Horizon.Sdk if (result.IsFailure) { response = default; + return result; } return CmifMessage.ParseResponse(out response, HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize).Memory.Span, false, 0); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs index 882115013..beaff613f 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs @@ -3,10 +3,10 @@ struct CmifDomainInHeader { public CmifDomainRequestType Type; - public byte ObjectsCount; - public ushort DataSize; - public int ObjectId; - public uint Padding; - public uint Token; + public byte ObjectsCount; + public ushort DataSize; + public int ObjectId; + public uint Padding; + public uint Token; } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs index b913db94e..1a02e0825 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs @@ -2,8 +2,8 @@ { enum CmifDomainRequestType : byte { - Invalid = 0, + Invalid = 0, SendMessage = 1, - Close = 2 + Close = 2 } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs index 781452e30..0d23d33bb 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { static class CmifMessage { - public const uint CmifInHeaderMagic = 0x49434653; // SFCI + public const uint CmifInHeaderMagic = 0x49434653; // SFCI public const uint CmifOutHeaderMagic = 0x4f434653; // SFCO public static CmifRequest CreateRequest(Span output, CmifRequestFormat format) @@ -21,27 +21,31 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif } totalSize += Unsafe.SizeOf() + format.DataSize; - totalSize = (totalSize + 1) & ~1; + totalSize = (totalSize + 1) & ~1; + int outPointerSizeTableOffset = totalSize; - int outPointerSizeTableSize = format.OutAutoBuffersCount + format.OutPointersCount; + int outPointerSizeTableSize = format.OutAutoBuffersCount + format.OutPointersCount; + totalSize += sizeof(ushort) * outPointerSizeTableSize; + int rawDataSizeInWords = (totalSize + sizeof(uint) - 1) / sizeof(uint); - CmifRequest request = new CmifRequest(); - - request.Hipc = HipcMessage.WriteMessage(output, new HipcMetadata() + CmifRequest request = new() { - Type = format.Context != 0 ? (int)CommandType.RequestWithContext : (int)CommandType.Request, - SendStaticsCount = format.InAutoBuffersCount + format.InPointersCount, - SendBuffersCount = format.InAutoBuffersCount + format.InBuffersCount, - ReceiveBuffersCount = format.OutAutoBuffersCount + format.OutBuffersCount, - ExchangeBuffersCount = format.InOutBuffersCount, - DataWordsCount = rawDataSizeInWords, - ReceiveStaticsCount = outPointerSizeTableSize + format.OutFixedPointersCount, - SendPid = format.SendPid, - CopyHandlesCount = format.HandlesCount, - MoveHandlesCount = 0 - }); + Hipc = HipcMessage.WriteMessage(output, new HipcMetadata() + { + Type = format.Context != 0 ? (int)CommandType.RequestWithContext : (int)CommandType.Request, + SendStaticsCount = format.InAutoBuffersCount + format.InPointersCount, + SendBuffersCount = format.InAutoBuffersCount + format.InBuffersCount, + ReceiveBuffersCount = format.OutAutoBuffersCount + format.OutBuffersCount, + ExchangeBuffersCount = format.InOutBuffersCount, + DataWordsCount = rawDataSizeInWords, + ReceiveStaticsCount = outPointerSizeTableSize + format.OutFixedPointersCount, + SendPid = format.SendPid, + CopyHandlesCount = format.HandlesCount, + MoveHandlesCount = 0 + }) + }; Span data = request.Hipc.DataWords; @@ -53,35 +57,36 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif domainHeader = new CmifDomainInHeader() { - Type = CmifDomainRequestType.SendMessage, + Type = CmifDomainRequestType.SendMessage, ObjectsCount = (byte)format.ObjectsCount, - DataSize = (ushort)payloadSize, - ObjectId = format.ObjectId, - Padding = 0, - Token = format.Context + DataSize = (ushort)payloadSize, + ObjectId = format.ObjectId, + Padding = 0, + Token = format.Context }; - data = data.Slice(Unsafe.SizeOf() / sizeof(uint)); + data = data[(Unsafe.SizeOf() / sizeof(uint))..]; - request.Objects = data.Slice((payloadSize + sizeof(uint) - 1) / sizeof(uint)); + request.Objects = data[((payloadSize + sizeof(uint) - 1) / sizeof(uint))..]; } ref CmifInHeader header = ref MemoryMarshal.Cast(data)[0]; header = new CmifInHeader() { - Magic = CmifInHeaderMagic, - Version = format.Context != 0 ? 1u : 0u, + Magic = CmifInHeaderMagic, + Version = format.Context != 0 ? 1u : 0u, CommandId = format.RequestId, - Token = format.ObjectId != 0 ? 0u : format.Context + Token = format.ObjectId != 0 ? 0u : format.Context }; - request.Data = MemoryMarshal.Cast(data).Slice(Unsafe.SizeOf()); + request.Data = MemoryMarshal.Cast(data)[Unsafe.SizeOf()..]; int paddingSizeBefore = (rawDataSizeInWords - request.Hipc.DataWords.Length) * sizeof(uint); - Span outPointerTable = MemoryMarshal.Cast(request.Hipc.DataWords).Slice(outPointerSizeTableOffset - paddingSizeBefore); - request.OutPointerSizes = MemoryMarshal.Cast(outPointerTable); + Span outPointerTable = MemoryMarshal.Cast(request.Hipc.DataWords)[(outPointerSizeTableOffset - paddingSizeBefore)..]; + + request.OutPointerSizes = MemoryMarshal.Cast(outPointerTable); request.ServerPointerSize = format.ServerPointerSize; return request; @@ -89,15 +94,15 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif public static Result ParseResponse(out CmifResponse response, Span input, bool isDomain, int size) { - HipcMessage responseMessage = new HipcMessage(input); + HipcMessage responseMessage = new(input); - Span data = MemoryMarshal.Cast(responseMessage.Data.DataWords); + Span data = MemoryMarshal.Cast(responseMessage.Data.DataWords); Span objects = Span.Empty; if (isDomain) { - data = data.Slice(Unsafe.SizeOf()); - objects = MemoryMarshal.Cast(data.Slice(Unsafe.SizeOf() + size)); + data = data[Unsafe.SizeOf()..]; + objects = MemoryMarshal.Cast(data[(Unsafe.SizeOf() + size)..]); } CmifOutHeader header = MemoryMarshal.Cast(data)[0]; @@ -105,19 +110,21 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif if (header.Magic != CmifOutHeaderMagic) { response = default; + return SfResult.InvalidOutHeader; } if (header.Result.IsFailure) { response = default; + return header.Result; } response = new CmifResponse() { - Data = data.Slice(Unsafe.SizeOf()), - Objects = objects, + Data = data[Unsafe.SizeOf()..], + Objects = objects, CopyHandles = responseMessage.Data.CopyHandles, MoveHandles = responseMessage.Data.MoveHandles }; @@ -125,4 +132,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif return Result.Success; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs index 2828cde5e..00b9d2bda 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs @@ -5,10 +5,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif struct CmifOutHeader { #pragma warning disable CS0649 - public uint Magic; - public uint Version; + public uint Magic; + public uint Version; public Result Result; - public uint Token; + public uint Token; #pragma warning restore CS0649 } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs index 80772ad33..e44a84ec1 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs @@ -6,9 +6,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif ref struct CmifRequest { public HipcMessageData Hipc; - public Span Data; - public Span OutPointerSizes; - public Span Objects; - public int ServerPointerSize; + public Span Data; + public Span OutPointerSizes; + public Span Objects; + public int ServerPointerSize; } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs index d11545786..592f11f42 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs @@ -3,21 +3,21 @@ struct CmifRequestFormat { #pragma warning disable CS0649 - public int ObjectId; + public int ObjectId; public uint RequestId; public uint Context; - public int DataSize; - public int ServerPointerSize; - public int InAutoBuffersCount; - public int OutAutoBuffersCount; - public int InBuffersCount; - public int OutBuffersCount; - public int InOutBuffersCount; - public int InPointersCount; - public int OutPointersCount; - public int OutFixedPointersCount; - public int ObjectsCount; - public int HandlesCount; + public int DataSize; + public int ServerPointerSize; + public int InAutoBuffersCount; + public int OutAutoBuffersCount; + public int InBuffersCount; + public int OutBuffersCount; + public int InOutBuffersCount; + public int InPointersCount; + public int OutPointersCount; + public int OutFixedPointersCount; + public int ObjectsCount; + public int HandlesCount; public bool SendPid; #pragma warning restore CS0649 } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs index d1d8dc9c5..2ff31eb67 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { public ReadOnlySpan Data; public ReadOnlySpan Objects; - public ReadOnlySpan CopyHandles; - public ReadOnlySpan MoveHandles; + public ReadOnlySpan CopyHandles; + public ReadOnlySpan MoveHandles; } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs index b3b05864e..82c0648b6 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs @@ -2,12 +2,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { enum CommandType { - Invalid = 0, - LegacyRequest = 1, - Close = 2, - LegacyControl = 3, - Request = 4, - Control = 5, + Invalid = 0, + LegacyRequest = 1, + Close = 2, + LegacyControl = 3, + Request = 4, + Control = 5, RequestWithContext = 6, ControlWithContext = 7 } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs index bcf311b2e..b0b4498d9 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif var inHeader = MemoryMarshal.Cast(inRawData)[0]; - ReadOnlySpan inDomainRawData = inRawData.Slice(Unsafe.SizeOf()); + ReadOnlySpan inDomainRawData = inRawData[Unsafe.SizeOf()..]; int targetObjectId = inHeader.ObjectId; @@ -39,7 +39,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif return SfResult.InvalidHeaderSize; } - ReadOnlySpan inMessageRawData = inDomainRawData.Slice(0, inHeader.DataSize); + ReadOnlySpan inMessageRawData = inDomainRawData[..inHeader.DataSize]; if (inHeader.ObjectsCount > DomainServiceObjectProcessor.MaximumObjects) { diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs index 92d86196a..796b8a789 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs @@ -63,7 +63,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif return SfResult.InvalidInObjectsCount; } - Result result = _domain.ReserveIds(new Span(_reservedObjectIds).Slice(0, OutObjectsCount)); + Result result = _domain.ReserveIds(new Span(_reservedObjectIds)[..OutObjectsCount]); if (result.IsFailure) { @@ -92,7 +92,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif DebugUtil.Assert(outHeaderSize + implOutDataTotalSize + OutObjectsCount * sizeof(int) <= outRawData.Length); - outRawData = outRawData.Slice(outHeaderSize); + outRawData = outRawData[outHeaderSize..]; _outObjectIdsOffset = (response.DataWords.Length * sizeof(uint) - outRawData.Length) + implOutDataTotalSize; return response; @@ -107,9 +107,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif DebugUtil.Assert(outHeaderSize + implOutDataTotalSize <= outRawData.Length); - outRawData = outRawData.Slice(outHeaderSize); + outRawData = outRawData[outHeaderSize..]; - _domain.UnreserveIds(new Span(_reservedObjectIds).Slice(0, OutObjectsCount)); + _domain.UnreserveIds(new Span(_reservedObjectIds)[..OutObjectsCount]); } public override void SetOutObjects(scoped ref ServiceDispatchContext context, HipcMessageData response, Span outObjects) @@ -129,7 +129,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif _domain.RegisterObject(objectIds[i], outObjects[i]); } - Span outObjectIds = MemoryMarshal.Cast(MemoryMarshal.Cast(response.DataWords).Slice(_outObjectIdsOffset)); + Span outObjectIds = MemoryMarshal.Cast(MemoryMarshal.Cast(response.DataWords)[_outObjectIdsOffset..]); for (int i = 0; i < outObjectsCount; i++) { diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs index 5af00077a..ad0e18244 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { struct PointerAndSize { - public static PointerAndSize Empty => new PointerAndSize(0UL, 0UL); + public static PointerAndSize Empty => new(0UL, 0UL); public ulong Address { get; } public ulong Size { get; } @@ -11,7 +11,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif public PointerAndSize(ulong address, ulong size) { Address = address; - Size = size; + Size = size; } } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs index 18a404302..6a92e8d55 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs @@ -2,28 +2,29 @@ { struct ServerMessageRuntimeMetadata { - public ushort InDataSize { get; } - public ushort OutDataSize { get; } - public byte InHeadersSize { get; } - public byte OutHeadersSize { get; } - public byte InObjectsCount { get; } - public byte OutObjectsCount { get; } + public ushort InDataSize { get; } + public ushort OutDataSize { get; } + public byte InHeadersSize { get; } + public byte OutHeadersSize { get; } + public byte InObjectsCount { get; } + public byte OutObjectsCount { get; } + public int UnfixedOutPointerSizeOffset => InDataSize + InHeadersSize + 0x10; public ServerMessageRuntimeMetadata( ushort inDataSize, ushort outDataSize, - byte inHeadersSize, - byte outHeadersSize, - byte inObjectsCount, - byte outObjectsCount) + byte inHeadersSize, + byte outHeadersSize, + byte inObjectsCount, + byte outObjectsCount) { - InDataSize = inDataSize; - OutDataSize = outDataSize; - InHeadersSize = inHeadersSize; - OutHeadersSize = outHeadersSize; - InObjectsCount = inObjectsCount; + InDataSize = inDataSize; + OutDataSize = outDataSize; + InHeadersSize = inHeadersSize; + OutHeadersSize = outHeadersSize; + InObjectsCount = inObjectsCount; OutObjectsCount = outObjectsCount; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs index 3339a1a60..31be810d3 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs @@ -5,14 +5,14 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif { ref struct ServiceDispatchContext { - public IServiceObject ServiceObject; - public ServerSessionManager Manager; - public ServerSession Session; + public IServiceObject ServiceObject; + public ServerSessionManager Manager; + public ServerSession Session; public ServerMessageProcessor Processor; - public HandlesToClose HandlesToClose; - public PointerAndSize PointerBuffer; - public ReadOnlySpan InMessageBuffer; - public Span OutMessageBuffer; - public HipcMessage Request; + public HandlesToClose HandlesToClose; + public PointerAndSize PointerBuffer; + public ReadOnlySpan InMessageBuffer; + public Span OutMessageBuffer; + public HipcMessage Request; } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs index 145c17839..21b342dff 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif public ServiceDispatchTable(string objectName, IReadOnlyDictionary entries) { _objectName = objectName; - _entries = entries; + _entries = entries; } public override Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan inRawData) @@ -30,4 +30,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif return new ServiceDispatchTable(instance.GetType().Name, instance.GetCommandHandlers()); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs index a0e28ca8f..816000675 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs @@ -39,17 +39,21 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif if (!entries.TryGetValue((int)commandId, out var commandHandler)) { - Logger.Warning?.Print(LogClass.KernelIpc, $"{objectName} command ID 0x{commandId:X} is not implemented"); - if (HorizonStatic.Options.IgnoreMissingServices) { // If ignore missing services is enabled, just pretend that everything is fine. - var response = PrepareForStubReply(ref context, out Span outRawData); + PrepareForStubReply(ref context, out Span outRawData); CommandHandler.GetCmifOutHeaderPointer(ref outHeader, ref outRawData); outHeader[0] = new CmifOutHeader() { Magic = CmifMessage.CmifOutHeaderMagic, Result = Result.Success }; + Logger.Warning?.Print(LogClass.Service, $"Missing service {objectName} (command ID: {commandId}) ignored"); + return Result.Success; } + else if (HorizonStatic.Options.ThrowOnInvalidCommandIds) + { + throw new NotImplementedException($"{objectName} command ID: {commandId} is not implemented"); + } return SfResult.UnknownCommandId; } @@ -72,6 +76,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif if (outHeader.IsEmpty) { commandResult.AbortOnSuccess(); + return commandResult; } @@ -80,11 +85,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif return Result.Success; } - private static HipcMessageData PrepareForStubReply(scoped ref ServiceDispatchContext context, out Span outRawData) + private static void PrepareForStubReply(scoped ref ServiceDispatchContext context, out Span outRawData) { var response = HipcMessage.WriteResponse(context.OutMessageBuffer, 0, 0x20 / sizeof(uint), 0, 0); outRawData = MemoryMarshal.Cast(response.DataWords); - return response; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs b/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs index 8f367b4e0..47aedde96 100644 --- a/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs +++ b/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs @@ -20,37 +20,37 @@ namespace Ryujinx.Horizon.Sdk.Sf struct CommandArg { - public CommandArgType Type { get; } - public HipcBufferFlags BufferFlags { get; } - public ushort BufferFixedSize { get; } - public int ArgSize { get; } - public int ArgAlignment { get; } + public CommandArgType Type { get; } + public HipcBufferFlags BufferFlags { get; } + public ushort BufferFixedSize { get; } + public int ArgSize { get; } + public int ArgAlignment { get; } public CommandArg(CommandArgType type) { - Type = type; - BufferFlags = default; + Type = type; + BufferFlags = default; BufferFixedSize = 0; - ArgSize = 0; - ArgAlignment = 0; + ArgSize = 0; + ArgAlignment = 0; } public CommandArg(CommandArgType type, int argSize, int argAlignment) { - Type = type; - BufferFlags = default; + Type = type; + BufferFlags = default; BufferFixedSize = 0; - ArgSize = argSize; - ArgAlignment = argAlignment; + ArgSize = argSize; + ArgAlignment = argAlignment; } public CommandArg(HipcBufferFlags flags, ushort fixedSize = 0) { - Type = CommandArgType.Buffer; - BufferFlags = flags; + Type = CommandArgType.Buffer; + BufferFlags = flags; BufferFixedSize = fixedSize; - ArgSize = 0; - ArgAlignment = 0; + ArgSize = 0; + ArgAlignment = 0; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs b/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs index 5b7c302f2..294c7d58d 100644 --- a/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs +++ b/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs @@ -6,8 +6,8 @@ namespace Ryujinx.Horizon.Sdk.Sf [AttributeUsage(AttributeTargets.Parameter)] class BufferAttribute : Attribute { - public HipcBufferFlags Flags { get; } - public ushort FixedSize { get; } + public HipcBufferFlags Flags { get; } + public ushort FixedSize { get; } public BufferAttribute(HipcBufferFlags flags) { @@ -16,7 +16,7 @@ namespace Ryujinx.Horizon.Sdk.Sf public BufferAttribute(HipcBufferFlags flags, ushort fixedSize) { - Flags = flags | HipcBufferFlags.FixedSize; + Flags = flags | HipcBufferFlags.FixedSize; FixedSize = fixedSize; } } @@ -35,4 +35,4 @@ namespace Ryujinx.Horizon.Sdk.Sf class MoveHandleAttribute : Attribute { } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs b/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs index ae42a8ef5..fe079d47d 100644 --- a/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs +++ b/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs @@ -9,20 +9,20 @@ namespace Ryujinx.Horizon.Sdk.Sf class CommandHandler { public delegate Result MethodInvoke( - ref ServiceDispatchContext context, - HipcCommandProcessor processor, + ref ServiceDispatchContext context, + HipcCommandProcessor processor, ServerMessageRuntimeMetadata runtimeMetadata, - ReadOnlySpan inRawData, - ref Span outHeader); + ReadOnlySpan inRawData, + ref Span outHeader); - private readonly MethodInvoke _invoke; + private readonly MethodInvoke _invoke; private readonly HipcCommandProcessor _processor; public string MethodName => _invoke.Method.Name; public CommandHandler(MethodInvoke invoke, params CommandArg[] args) { - _invoke = invoke; + _invoke = invoke; _processor = new HipcCommandProcessor(args); } @@ -37,8 +37,8 @@ namespace Ryujinx.Horizon.Sdk.Sf context.Processor.SetImplementationProcessor(_processor); } - var runtimeMetadata = context.Processor.GetRuntimeMetadata(); - Result result = context.Processor.PrepareForProcess(ref context, runtimeMetadata); + var runtimeMetadata = context.Processor.GetRuntimeMetadata(); + Result result = context.Processor.PrepareForProcess(ref context, runtimeMetadata); if (result.IsFailure) { @@ -50,8 +50,8 @@ namespace Ryujinx.Horizon.Sdk.Sf public static void GetCmifOutHeaderPointer(ref Span outHeader, ref Span outRawData) { - outHeader = MemoryMarshal.Cast(outRawData).Slice(0, 1); - outRawData = outRawData.Slice(Unsafe.SizeOf()); + outHeader = MemoryMarshal.Cast(outRawData)[..1]; + outRawData = outRawData[Unsafe.SizeOf()..]; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs b/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs index 9a3a511ad..feb2d666f 100644 --- a/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs +++ b/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs @@ -22,6 +22,7 @@ namespace Ryujinx.Horizon.Sdk.Sf public static ref T GetRef(PointerAndSize bufferRange) where T : unmanaged { var writableRegion = GetWritableRegion(bufferRange); + return ref MemoryMarshal.Cast(writableRegion.Memory.Span)[0]; } @@ -65,4 +66,4 @@ namespace Ryujinx.Horizon.Sdk.Sf response.MoveHandles[index] = value; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs index 594af2c84..269ab4fe5 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs @@ -5,12 +5,12 @@ 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, + 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 index 4ef6374ba..b1e67253c 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs @@ -2,9 +2,9 @@ { enum HipcBufferMode { - Normal = 0, + Normal = 0, NonSecure = 1, - Invalid = 2, + Invalid = 2, NonDevice = 3 } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs index ea2ec6505..7541e2941 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs @@ -61,7 +61,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { clientHandle = 0; - if (!(_session.ServiceObjectHolder.ServiceObject is DomainServiceObject domain)) + if (_session.ServiceObjectHolder.ServiceObject is not DomainServiceObject domain) { return HipcResult.TargetNotDomain; } @@ -112,4 +112,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc return Result.Success; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs index 3017f4041..6500d6cf7 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs @@ -10,9 +10,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { public const int AutoReceiveStatic = byte.MaxValue; - public HipcMetadata Meta; + public HipcMetadata Meta; public HipcMessageData Data; - public ulong Pid; + public ulong Pid; public HipcMessage(Span data) { @@ -20,10 +20,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc Header header = MemoryMarshal.Cast(data)[0]; - data = data.Slice(Unsafe.SizeOf
()); + data = data[Unsafe.SizeOf
()..]; - int receiveStaticsCount = 0; - ulong pid = 0; + int receiveStaticsCount = 0; + ulong pid = 0; if (header.ReceiveStaticMode != 0) { @@ -42,46 +42,44 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc if (header.HasSpecialHeader) { specialHeader = MemoryMarshal.Cast(data)[0]; - - data = data.Slice(Unsafe.SizeOf()); + data = data[Unsafe.SizeOf()..]; if (specialHeader.SendPid) { - pid = MemoryMarshal.Cast(data)[0]; - - data = data.Slice(sizeof(ulong)); + pid = MemoryMarshal.Cast(data)[0]; + data = data[sizeof(ulong)..]; } } Meta = new HipcMetadata() { - Type = (int)header.Type, - SendStaticsCount = header.SendStaticsCount, - SendBuffersCount = header.SendBuffersCount, - ReceiveBuffersCount = header.ReceiveBuffersCount, + 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 + DataWordsCount = header.DataWordsCount, + ReceiveStaticsCount = receiveStaticsCount, + SendPid = specialHeader.SendPid, + CopyHandlesCount = specialHeader.CopyHandlesCount, + MoveHandlesCount = specialHeader.MoveHandlesCount }; Data = CreateMessageData(Meta, data, initialLength); - Pid = pid; + Pid = pid; } public static HipcMessageData WriteResponse( Span destination, - int sendStaticCount, - int dataWordsCount, - int copyHandlesCount, - int moveHandlesCount) + int sendStaticCount, + int dataWordsCount, + int copyHandlesCount, + int moveHandlesCount) { return WriteMessage(destination, new HipcMetadata() { SendStaticsCount = sendStaticCount, - DataWordsCount = dataWordsCount, + DataWordsCount = dataWordsCount, CopyHandlesCount = copyHandlesCount, MoveHandlesCount = moveHandlesCount }); @@ -89,38 +87,37 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc public static HipcMessageData WriteMessage(Span destination, HipcMetadata meta) { - int initialLength = destination.Length; - + int initialLength = destination.Length; bool hasSpecialHeader = meta.SendPid || meta.CopyHandlesCount != 0 || meta.MoveHandlesCount != 0; MemoryMarshal.Cast(destination)[0] = new Header() { - Type = (CommandType)meta.Type, - SendStaticsCount = meta.SendStaticsCount, - SendBuffersCount = meta.SendBuffersCount, - ReceiveBuffersCount = meta.ReceiveBuffersCount, + 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 + DataWordsCount = meta.DataWordsCount, + ReceiveStaticMode = meta.ReceiveStaticsCount != 0 ? (meta.ReceiveStaticsCount != AutoReceiveStatic ? meta.ReceiveStaticsCount + 2 : 2) : 0, + HasSpecialHeader = hasSpecialHeader }; - destination = destination.Slice(Unsafe.SizeOf
()); + destination = destination[Unsafe.SizeOf
()..]; if (hasSpecialHeader) { MemoryMarshal.Cast(destination)[0] = new SpecialHeader() { - SendPid = meta.SendPid, + SendPid = meta.SendPid, CopyHandlesCount = meta.CopyHandlesCount, MoveHandlesCount = meta.MoveHandlesCount }; - destination = destination.Slice(Unsafe.SizeOf()); + destination = destination[Unsafe.SizeOf()..]; if (meta.SendPid) { - destination = destination.Slice(sizeof(ulong)); + destination = destination[sizeof(ulong)..]; } } @@ -133,68 +130,67 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc if (meta.CopyHandlesCount != 0) { - copyHandles = MemoryMarshal.Cast(data).Slice(0, meta.CopyHandlesCount); + copyHandles = MemoryMarshal.Cast(data)[..meta.CopyHandlesCount]; - data = data.Slice(meta.CopyHandlesCount * sizeof(int)); + data = data[(meta.CopyHandlesCount * sizeof(int))..]; } Span moveHandles = Span.Empty; if (meta.MoveHandlesCount != 0) { - moveHandles = MemoryMarshal.Cast(data).Slice(0, meta.MoveHandlesCount); + moveHandles = MemoryMarshal.Cast(data)[..meta.MoveHandlesCount]; - data = data.Slice(meta.MoveHandlesCount * sizeof(int)); + data = data[(meta.MoveHandlesCount * sizeof(int))..]; } Span sendStatics = Span.Empty; if (meta.SendStaticsCount != 0) { - sendStatics = MemoryMarshal.Cast(data).Slice(0, meta.SendStaticsCount); + sendStatics = MemoryMarshal.Cast(data)[..meta.SendStaticsCount]; - data = data.Slice(meta.SendStaticsCount * Unsafe.SizeOf()); + data = data[(meta.SendStaticsCount * Unsafe.SizeOf())..]; } Span sendBuffers = Span.Empty; if (meta.SendBuffersCount != 0) { - sendBuffers = MemoryMarshal.Cast(data).Slice(0, meta.SendBuffersCount); + sendBuffers = MemoryMarshal.Cast(data)[..meta.SendBuffersCount]; - data = data.Slice(meta.SendBuffersCount * Unsafe.SizeOf()); + data = data[(meta.SendBuffersCount * Unsafe.SizeOf())..]; } Span receiveBuffers = Span.Empty; if (meta.ReceiveBuffersCount != 0) { - receiveBuffers = MemoryMarshal.Cast(data).Slice(0, meta.ReceiveBuffersCount); + receiveBuffers = MemoryMarshal.Cast(data)[..meta.ReceiveBuffersCount]; - data = data.Slice(meta.ReceiveBuffersCount * Unsafe.SizeOf()); + data = data[(meta.ReceiveBuffersCount * Unsafe.SizeOf())..]; } Span exchangeBuffers = Span.Empty; if (meta.ExchangeBuffersCount != 0) { - exchangeBuffers = MemoryMarshal.Cast(data).Slice(0, meta.ExchangeBuffersCount); + exchangeBuffers = MemoryMarshal.Cast(data)[..meta.ExchangeBuffersCount]; - data = data.Slice(meta.ExchangeBuffersCount * Unsafe.SizeOf()); + data = data[(meta.ExchangeBuffersCount * Unsafe.SizeOf())..]; } Span dataWords = Span.Empty; if (meta.DataWordsCount != 0) { - int dataOffset = initialLength - data.Length; + int dataOffset = initialLength - data.Length; int dataOffsetAligned = BitUtils.AlignUp(dataOffset, 0x10); + int padding = (dataOffsetAligned - dataOffset) / sizeof(uint); - int padding = (dataOffsetAligned - dataOffset) / sizeof(uint); + dataWords = MemoryMarshal.Cast(data)[padding..meta.DataWordsCount]; - dataWords = MemoryMarshal.Cast(data).Slice(padding, meta.DataWordsCount - padding); - - data = data.Slice(meta.DataWordsCount * sizeof(uint)); + data = data[(meta.DataWordsCount * sizeof(uint))..]; } Span receiveList = Span.Empty; @@ -203,19 +199,19 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { int receiveListSize = meta.ReceiveStaticsCount == AutoReceiveStatic ? 1 : meta.ReceiveStaticsCount; - receiveList = MemoryMarshal.Cast(data).Slice(0, receiveListSize); + receiveList = MemoryMarshal.Cast(data)[..receiveListSize]; } return new HipcMessageData() { - SendStatics = sendStatics, - SendBuffers = sendBuffers, - ReceiveBuffers = receiveBuffers, + SendStatics = sendStatics, + SendBuffers = sendBuffers, + ReceiveBuffers = receiveBuffers, ExchangeBuffers = exchangeBuffers, - DataWords = dataWords, - ReceiveList = receiveList, - CopyHandles = copyHandles, - MoveHandles = moveHandles + 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 index c83c422ca..154b8f079 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs @@ -8,9 +8,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc public Span SendBuffers; public Span ReceiveBuffers; public Span ExchangeBuffers; - public Span DataWords; + public Span DataWords; public Span ReceiveList; - public Span CopyHandles; - public Span MoveHandles; + public Span CopyHandles; + public Span MoveHandles; } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs index fe13137a6..10abc4006 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs @@ -2,15 +2,15 @@ { struct HipcMetadata { - public int Type; - public int SendStaticsCount; - public int SendBuffersCount; - public int ReceiveBuffersCount; - public int ExchangeBuffersCount; - public int DataWordsCount; - public int ReceiveStaticsCount; + 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; + public int CopyHandlesCount; + public int MoveHandlesCount; } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs index 94bf09685..56cf16fb0 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs @@ -8,7 +8,7 @@ public HipcReceiveListEntry(ulong address, ulong size) { _addressLow = (uint)address; - _word1 = (ushort)(address >> 32) | (uint)(size << 16); + _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 index ef989a986..3b483be81 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs @@ -6,17 +6,14 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { 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); + public static Result OutOfSessionMemory => new(ModuleId, 102); + public static Result OutOfSessions => new(ModuleId, 131); + public static Result PointerBufferTooSmall => new(ModuleId, 141); + public static Result OutOfDomains => new(ModuleId, 200); + public static Result InvalidRequestSize => new(ModuleId, 402); + public static Result UnknownCommandType => new(ModuleId, 403); + public static Result InvalidCmifRequest => new(ModuleId, 420); + public static Result TargetNotDomain => new(ModuleId, 491); + public static Result DomainObjectNotFound => new(ModuleId, 492); } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs index 5cebf47c8..103820a6b 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs @@ -4,9 +4,9 @@ { 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 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) { @@ -19,4 +19,4 @@ _data = data; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs index e087cb223..b99d63c5e 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs @@ -2,19 +2,19 @@ { struct ManagerOptions { - public static ManagerOptions Default => new ManagerOptions(0, 0, 0, false); + public static ManagerOptions Default => new(0, 0, 0, false); - public int PointerBufferSize { get; } - public int MaxDomains { get; } - public int MaxDomainObjects { get; } + 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; + PointerBufferSize = pointerBufferSize; + MaxDomains = maxDomains; + MaxDomainObjects = maxDomainObjects; CanDeferInvokeRequest = canDeferInvokeRequest; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs index 923f2d52d..bbbab8985 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs @@ -6,22 +6,22 @@ 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 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, + int portIndex, + int portHandle, + ServiceName name, + bool managed, ServiceObjectHolder staticHoder) : base(portHandle) { PortHandle = portHandle; - Name = name; - Managed = managed; + Name = name; + Managed = managed; if (staticHoder != null) { diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs index d920a6591..dda775397 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc protected override Result DispatchManagerRequest(ServerSession session, Span inMessage, Span outMessage) { - HipcManager hipcManager = new HipcManager(this, session); + HipcManager hipcManager = new(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 index 5bb2de25d..2ca9ceea2 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs @@ -80,7 +80,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc return null; } - ServerSession session = new ServerSession(sessionIndex, sessionHandle, obj); + ServerSession session = new(sessionIndex, sessionHandle, obj); _sessions.Add(session); @@ -111,7 +111,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { lock (_resourceLock) { - Server server = new Server(portIndex, portHandle, name, managed, staticHoder); + Server server = new(portIndex, portHandle, name, managed, staticHoder); _servers.Add(server); diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs index 68cae6bc4..9d21290d8 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc private enum UserDataTag { - Server = 1, + Server = 1, Session = 2 } @@ -36,16 +36,17 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc _canDeferInvokeRequest = options.CanDeferInvokeRequest; _multiWait = new MultiWait(); - _waitList = new MultiWait(); + _waitList = new MultiWait(); _multiWaitSelectionLock = new object(); - _waitListLock = new object(); + _waitListLock = new object(); _requestStopEvent = new Event(EventClearMode.ManualClear); - _notifyEvent = new Event(EventClearMode.ManualClear); + _notifyEvent = new Event(EventClearMode.ManualClear); _requestStopEventHolder = new MultiWaitHolderOfEvent(_requestStopEvent); _multiWait.LinkMultiWaitHolder(_requestStopEventHolder); + _notifyEventHolder = new MultiWaitHolderOfEvent(_notifyEvent); _multiWait.LinkMultiWaitHolder(_notifyEventHolder); } @@ -73,6 +74,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc private void RegisterServerImpl(int portIndex, ServiceObjectHolder staticHolder, int portHandle) { Server server = AllocateServer(portIndex, portHandle, ServiceName.Invalid, managed: false, staticHolder); + RegisterServerImpl(server); } @@ -86,6 +88,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc } Server server = AllocateServer(portIndex, portHandle, name, managed: true, staticHolder); + RegisterServerImpl(server); return Result.Success; @@ -103,6 +106,11 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc throw new NotSupportedException(); } + protected Result AcceptImpl(Server server, IServiceObject obj) + { + return AcceptSession(server.PortHandle, new ServiceObjectHolder(obj)); + } + public void ServiceRequests() { while (WaitAndProcessRequestsImpl()); @@ -175,7 +183,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc protected override void RegisterSessionToWaitList(ServerSession session) { session.HasReceived = false; - session.UserData = UserDataTag.Session; + session.UserData = UserDataTag.Session; + RegisterToWaitList(session); } @@ -198,15 +207,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc private Result Process(MultiWaitHolder holder) { - switch ((UserDataTag)holder.UserData) + return (UserDataTag)holder.UserData switch { - case UserDataTag.Server: - return ProcessForServer(holder); - case UserDataTag.Session: - return ProcessForSession(holder); - default: - throw new NotImplementedException(((UserDataTag)holder.UserData).ToString()); - } + UserDataTag.Server => ProcessForServer(holder), + UserDataTag.Session => ProcessForSession(holder), + _ => throw new NotImplementedException(((UserDataTag)holder.UserData).ToString()) + }; } private Result ProcessForServer(MultiWaitHolder holder) @@ -259,6 +265,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc } session.HasReceived = true; + tlsMessage.Memory.Span.CopyTo(savedMessage.Memory.Span); } else diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs index eb98fefd0..a17300823 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs @@ -6,18 +6,18 @@ 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 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; + SessionIndex = index; + SessionHandle = handle; } } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs index e85892f26..6d3950813 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs @@ -75,9 +75,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc } session.PointerBuffer = GetSessionPointerBuffer(session); - session.SavedMessage = GetSessionSavedMessageBuffer(session); + session.SavedMessage = GetSessionSavedMessageBuffer(session); RegisterSessionToWaitList(session); + return Result.Success; } @@ -109,10 +110,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc } protected virtual Server AllocateServer( - int portIndex, - int portHandle, - ServiceName name, - bool managed, + int portIndex, + int portHandle, + ServiceName name, + bool managed, ServiceObjectHolder staticHoder) { throw new NotSupportedException(); @@ -141,6 +142,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc protected void CloseSessionImpl(ServerSession session) { int sessionHandle = session.Handle; + Os.FinalizeMultiWaitHolder(session); DestroySession(session); HorizonStatic.Syscall.CloseHandle(sessionHandle).AbortOnFailure(); @@ -156,6 +158,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc if (session.IsClosed || GetCmifCommandType(message) == CommandType.Close) { CloseSessionImpl(session); + return Result.Success; } else @@ -165,6 +168,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc if (result.IsSuccess) { RegisterSessionToWaitList(session); + return Result.Success; } else if (SfResult.RequestContextChanged(result)) @@ -176,6 +180,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc Logger.Warning?.Print(LogClass.KernelIpc, $"Request processing returned error {result}"); CloseSessionImpl(session); + return Result.Success; } } @@ -197,8 +202,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc return DispatchManagerRequest(session, inMessage, outMessage); default: return HipcResult.UnknownCommandType; - } } + } private static int GetInlineContext(CommandType commandType, ReadOnlySpan inMessage) { @@ -231,7 +236,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc { HipcMessageData messageData = HipcMessage.WriteMessage(message, new HipcMetadata() { - Type = (int)CommandType.Invalid, + Type = (int)CommandType.Invalid, ReceiveStaticsCount = HipcMessage.AutoReceiveStatic }); @@ -271,9 +276,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc protected virtual Result DispatchRequest( ServiceObjectHolder objectHolder, - ServerSession session, - Span inMessage, - Span outMessage) + ServerSession session, + Span inMessage, + Span outMessage) { HipcMessage request; @@ -288,14 +293,14 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc var dispatchCtx = new ServiceDispatchContext() { - ServiceObject = objectHolder.ServiceObject, - Manager = this, - Session = session, - HandlesToClose = new HandlesToClose(), - PointerBuffer = session.PointerBuffer, - InMessageBuffer = inMessage, + ServiceObject = objectHolder.ServiceObject, + Manager = this, + Session = session, + HandlesToClose = new HandlesToClose(), + PointerBuffer = session.PointerBuffer, + InMessageBuffer = inMessage, OutMessageBuffer = outMessage, - Request = request + Request = request }; ReadOnlySpan inRawData = MemoryMarshal.Cast(dispatchCtx.Request.Data.DataWords); @@ -332,4 +337,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc return this; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs b/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs index 53202edee..6bba49ae0 100644 --- a/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs +++ b/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs @@ -136,9 +136,9 @@ namespace Ryujinx.Horizon.Sdk.Sf ulong pointerBufferTail = context.PointerBuffer.Address; ulong pointerBufferHead = pointerBufferTail + context.PointerBuffer.Size; - int sendMapAliasIndex = 0; - int recvMapAliasIndex = 0; - int sendPointerIndex = 0; + int sendMapAliasIndex = 0; + int recvMapAliasIndex = 0; + int sendPointerIndex = 0; int unfixedRecvPointerIndex = 0; for (int i = 0; i < _args.Length; i++) @@ -188,8 +188,9 @@ namespace Ryujinx.Horizon.Sdk.Sf if (flags.HasFlag(HipcBufferFlags.In)) { var descriptor = context.Request.Data.SendStatics[sendPointerIndex++]; - ulong address = descriptor.Address; - ulong size = descriptor.Size; + ulong address = descriptor.Address; + ulong size = descriptor.Size; + _bufferRanges[i] = new PointerAndSize(address, size); if (size != 0) @@ -207,13 +208,14 @@ namespace Ryujinx.Horizon.Sdk.Sf } else { - var data = MemoryMarshal.Cast(context.Request.Data.DataWords); - var recvPointerSizes = MemoryMarshal.Cast(data.Slice(runtimeMetadata.UnfixedOutPointerSizeOffset)); + var data = MemoryMarshal.Cast(context.Request.Data.DataWords); + var recvPointerSizes = MemoryMarshal.Cast(data[runtimeMetadata.UnfixedOutPointerSizeOffset..]); + size = recvPointerSizes[unfixedRecvPointerIndex++]; } pointerBufferHead = BitUtils.AlignDown(pointerBufferHead - size, 0x10UL); - _bufferRanges[i] = new PointerAndSize(pointerBufferHead, size); + _bufferRanges[i] = new PointerAndSize(pointerBufferHead, size); } } } @@ -304,16 +306,17 @@ namespace Ryujinx.Horizon.Sdk.Sf { 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.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; + 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; @@ -340,7 +343,7 @@ namespace Ryujinx.Horizon.Sdk.Sf { if (_args[i].Type == CommandArgType.InObject) { - int index = inObjectIndex++; + int index = inObjectIndex++; var inObject = inObjects[index]; objects[index] = inObject?.ServiceObject; @@ -365,6 +368,7 @@ namespace Ryujinx.Horizon.Sdk.Sf _outCopyHandlesCount, _outMoveHandlesCount + runtimeMetadata.OutObjectsCount); outRawData = MemoryMarshal.Cast(response.DataWords); + return response; } @@ -377,6 +381,7 @@ namespace Ryujinx.Horizon.Sdk.Sf (BitUtils.AlignUp(rawDataSize, 4) + 0x10) / sizeof(uint), 0, 0); + outRawData = MemoryMarshal.Cast(response.DataWords); } @@ -410,6 +415,7 @@ namespace Ryujinx.Horizon.Sdk.Sf if (obj == null) { response.MoveHandles[index] = 0; + return; } @@ -418,4 +424,4 @@ namespace Ryujinx.Horizon.Sdk.Sf response.MoveHandles[index] = clientHandle; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs b/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs index 982f454f0..10e4f9094 100644 --- a/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs +++ b/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs @@ -12,24 +12,22 @@ namespace Ryujinx.Horizon.Sdk.Sf { int argsCount = args.Length; - int[] sizes = new int[argsCount]; + int[] sizes = new int[argsCount]; int[] aligns = new int[argsCount]; - int[] map = new int[argsCount]; + int[] map = new int[argsCount]; for (int i = 0; i < argsCount; i++) { - sizes[i] = args[i].ArgSize; + sizes[i] = args[i].ArgSize; aligns[i] = args[i].ArgAlignment; - map[i] = i; + map[i] = i; } for (int i = 1; i < argsCount; i++) { for (int j = i; j > 0 && aligns[map[j - 1]] > aligns[map[j]]; j--) { - var temp = map[j - 1]; - map[j - 1] = map[j]; - map[j] = temp; + (map[j], map[j - 1]) = (map[j - 1], map[j]); } } @@ -37,9 +35,9 @@ namespace Ryujinx.Horizon.Sdk.Sf foreach (int i in map) { - offset = BitUtils.AlignUp(offset, aligns[i]); + offset = BitUtils.AlignUp(offset, aligns[i]); offsets[i] = offset; - offset += sizes[i]; + offset += sizes[i]; } offsets[argsCount] = offset; @@ -48,4 +46,4 @@ namespace Ryujinx.Horizon.Sdk.Sf return offsets; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sf/SfResult.cs b/Ryujinx.Horizon/Sdk/Sf/SfResult.cs index 6aa11ba5f..72502d17e 100644 --- a/Ryujinx.Horizon/Sdk/Sf/SfResult.cs +++ b/Ryujinx.Horizon/Sdk/Sf/SfResult.cs @@ -6,26 +6,22 @@ namespace Ryujinx.Horizon.Sdk.Sf { public const int ModuleId = 10; - public static Result NotSupported => new Result(ModuleId, 1); - public static Result InvalidHeaderSize => new Result(ModuleId, 202); - public static Result InvalidInHeader => new Result(ModuleId, 211); - public static Result InvalidOutHeader => new Result(ModuleId, 212); - public static Result UnknownCommandId => new Result(ModuleId, 221); - public static Result InvalidOutRawSize => new Result(ModuleId, 232); - public static Result InvalidInObjectsCount => new Result(ModuleId, 235); - public static Result InvalidOutObjectsCount => new Result(ModuleId, 236); - public static Result InvalidInObject => new Result(ModuleId, 239); - - public static Result TargetNotFound => new Result(ModuleId, 261); - - public static Result OutOfDomainEntries => new Result(ModuleId, 301); - - public static Result InvalidatedByUser => new Result(ModuleId, 802); - public static Result RequestDeferredByUser => new Result(ModuleId, 812); + public static Result NotSupported => new(ModuleId, 1); + public static Result InvalidHeaderSize => new(ModuleId, 202); + public static Result InvalidInHeader => new(ModuleId, 211); + public static Result InvalidOutHeader => new(ModuleId, 212); + public static Result UnknownCommandId => new(ModuleId, 221); + public static Result InvalidOutRawSize => new(ModuleId, 232); + public static Result InvalidInObjectsCount => new(ModuleId, 235); + public static Result InvalidOutObjectsCount => new(ModuleId, 236); + public static Result InvalidInObject => new(ModuleId, 239); + public static Result TargetNotFound => new(ModuleId, 261); + public static Result OutOfDomainEntries => new(ModuleId, 301); + public static Result InvalidatedByUser => new(ModuleId, 802); + public static Result RequestDeferredByUser => new(ModuleId, 812); public static bool RequestContextChanged(Result result) => result.InRange(800, 899); - public static bool Invalidated(Result result) => result.InRange(801, 809); - - public static bool RequestDeferred(Result result) => result.InRange(811, 819); + public static bool Invalidated(Result result) => result.InRange(801, 809); + public static bool RequestDeferred(Result result) => result.InRange(811, 819); } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs b/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs new file mode 100644 index 000000000..644285834 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs @@ -0,0 +1,8 @@ +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Sm +{ + interface IManagerService : IServiceObject + { + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sm/IUserService.cs b/Ryujinx.Horizon/Sdk/Sm/IUserService.cs new file mode 100644 index 000000000..ad9bc9d7b --- /dev/null +++ b/Ryujinx.Horizon/Sdk/Sm/IUserService.cs @@ -0,0 +1,13 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Sm +{ + interface IUserService : IServiceObject + { + Result Initialize(ulong clientProcessId); + Result GetService(out int handle, ServiceName name); + Result RegisterService(out int handle, ServiceName name, int maxSessions, bool isLight); + Result UnregisterService(ServiceName name); + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sdk/Sm/SmApi.cs b/Ryujinx.Horizon/Sdk/Sm/SmApi.cs index e4b0eea1c..533e68d9c 100644 --- a/Ryujinx.Horizon/Sdk/Sm/SmApi.cs +++ b/Ryujinx.Horizon/Sdk/Sm/SmApi.cs @@ -7,16 +7,18 @@ namespace Ryujinx.Horizon.Sdk.Sm { class SmApi { + private const string SmName = "sm:"; + private int _portHandle; public Result Initialize() { - Result result = HorizonStatic.Syscall.ConnectToNamedPort(out int portHandle, "sm:"); + Result result = HorizonStatic.Syscall.ConnectToNamedPort(out int portHandle, SmName); while (result == KernelResult.NotFound) { HorizonStatic.Syscall.SleepThread(50000000L); - result = HorizonStatic.Syscall.ConnectToNamedPort(out portHandle, "sm:"); + result = HorizonStatic.Syscall.ConnectToNamedPort(out portHandle, SmName); } if (result.IsFailure) @@ -33,7 +35,7 @@ namespace Ryujinx.Horizon.Sdk.Sm { Span data = stackalloc byte[8]; - SpanWriter writer = new SpanWriter(data); + SpanWriter writer = new(data); writer.Write(0UL); @@ -44,7 +46,7 @@ namespace Ryujinx.Horizon.Sdk.Sm { Span data = stackalloc byte[8]; - SpanWriter writer = new SpanWriter(data); + SpanWriter writer = new(data); writer.Write(name); @@ -53,10 +55,12 @@ namespace Ryujinx.Horizon.Sdk.Sm if (result.IsFailure) { handle = 0; + return result; } handle = response.MoveHandles[0]; + return Result.Success; } @@ -64,7 +68,7 @@ namespace Ryujinx.Horizon.Sdk.Sm { Span data = stackalloc byte[16]; - SpanWriter writer = new SpanWriter(data); + SpanWriter writer = new(data); writer.Write(name); writer.Write(isLight ? 1 : 0); @@ -75,10 +79,12 @@ namespace Ryujinx.Horizon.Sdk.Sm if (result.IsFailure) { handle = 0; + return result; } handle = response.MoveHandles[0]; + return Result.Success; } @@ -86,7 +92,7 @@ namespace Ryujinx.Horizon.Sdk.Sm { Span data = stackalloc byte[8]; - SpanWriter writer = new SpanWriter(data); + SpanWriter writer = new(data); writer.Write(name); @@ -97,11 +103,11 @@ namespace Ryujinx.Horizon.Sdk.Sm { Span data = stackalloc byte[8]; - SpanWriter writer = new SpanWriter(data); + SpanWriter writer = new(data); writer.Write(0UL); return ServiceUtil.SendRequest(out _, _portHandle, 4, sendPid: true, data); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/ServiceEntry.cs b/Ryujinx.Horizon/ServiceEntry.cs index 3fea46c19..06152d9ff 100644 --- a/Ryujinx.Horizon/ServiceEntry.cs +++ b/Ryujinx.Horizon/ServiceEntry.cs @@ -6,20 +6,22 @@ namespace Ryujinx.Horizon { public struct ServiceEntry { - private readonly Action _entrypoint; - private readonly HorizonOptions _options; + private readonly Action _entrypoint; + private readonly ServiceTable _serviceTable; + private readonly HorizonOptions _options; - internal ServiceEntry(Action entrypoint, HorizonOptions options) + internal ServiceEntry(Action entrypoint, ServiceTable serviceTable, HorizonOptions options) { - _entrypoint = entrypoint; - _options = options; + _entrypoint = entrypoint; + _serviceTable = serviceTable; + _options = options; } public void Start(ISyscallApi syscallApi, IVirtualMemoryManager addressSpace, IThreadContext threadContext) { HorizonStatic.Register(_options, syscallApi, addressSpace, threadContext, (int)threadContext.GetX(1)); - _entrypoint(); + _entrypoint(_serviceTable); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/ServiceTable.cs b/Ryujinx.Horizon/ServiceTable.cs index 933b6a59b..2edc6ea10 100644 --- a/Ryujinx.Horizon/ServiceTable.cs +++ b/Ryujinx.Horizon/ServiceTable.cs @@ -1,22 +1,58 @@ using Ryujinx.Horizon.LogManager; +using Ryujinx.Horizon.Prepo; using System.Collections.Generic; +using System.Threading; namespace Ryujinx.Horizon { - public static class ServiceTable + public class ServiceTable { - public static IEnumerable GetServices(HorizonOptions options) + private int _readyServices; + private int _totalServices; + + private readonly ManualResetEvent _servicesReadyEvent = new(false); + + public IEnumerable GetServices(HorizonOptions options) { - List entries = new List(); + List entries = new(); void RegisterService() where T : IService { - entries.Add(new ServiceEntry(T.Main, options)); + entries.Add(new ServiceEntry(T.Main, this, options)); } RegisterService(); + RegisterService(); + + _totalServices = entries.Count; return entries; } + + internal void SignalServiceReady() + { + if (Interlocked.Increment(ref _readyServices) == _totalServices) + { + _servicesReadyEvent.Set(); + } + } + + public void WaitServicesReady() + { + _servicesReadyEvent.WaitOne(); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _servicesReadyEvent.Dispose(); + } + } + + public void Dispose() + { + Dispose(true); + } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs b/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs index fed420aa8..50c18a2c9 100644 --- a/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs +++ b/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs @@ -5,16 +5,16 @@ namespace Ryujinx.Horizon.Sm.Impl struct ServiceInfo { public ServiceName Name; - public ulong OwnerProcessId; - public int PortHandle; + public ulong OwnerProcessId; + public int PortHandle; public void Free() { HorizonStatic.Syscall.CloseHandle(PortHandle); - Name = ServiceName.Invalid; + Name = ServiceName.Invalid; OwnerProcessId = 0L; - PortHandle = 0; + PortHandle = 0; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs b/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs index cdf2d17f1..44a1ec46e 100644 --- a/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs +++ b/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs @@ -107,8 +107,8 @@ namespace Ryujinx.Horizon.Sm.Impl return result; } - freeService.PortHandle = clientPort; - freeService.Name = name; + freeService.PortHandle = clientPort; + freeService.Name = name; freeService.OwnerProcessId = processId; return Result.Success; @@ -126,20 +126,19 @@ namespace Ryujinx.Horizon.Sm.Impl // TODO: Validation with GetProcessInfo etc. int serviceIndex = GetServiceInfo(name); - if (serviceIndex < 0) { return SmResult.NotRegistered; } ref var serviceInfo = ref _services[serviceIndex]; - if (serviceInfo.OwnerProcessId != processId) { return SmResult.NotAllowed; } serviceInfo.Free(); + return Result.Success; } @@ -194,4 +193,4 @@ namespace Ryujinx.Horizon.Sm.Impl return -1; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sm/Ipc/ManagerService.cs b/Ryujinx.Horizon/Sm/Ipc/ManagerService.cs new file mode 100644 index 000000000..c7dcddc91 --- /dev/null +++ b/Ryujinx.Horizon/Sm/Ipc/ManagerService.cs @@ -0,0 +1,8 @@ +using Ryujinx.Horizon.Sdk.Sm; + +namespace Ryujinx.Horizon.Sm.Ipc +{ + partial class ManagerService : IManagerService + { + } +} diff --git a/Ryujinx.Horizon/Sm/UserService.cs b/Ryujinx.Horizon/Sm/Ipc/UserService.cs similarity index 91% rename from Ryujinx.Horizon/Sm/UserService.cs rename to Ryujinx.Horizon/Sm/Ipc/UserService.cs index d3b4537bf..d093913a9 100644 --- a/Ryujinx.Horizon/Sm/UserService.cs +++ b/Ryujinx.Horizon/Sm/Ipc/UserService.cs @@ -3,14 +3,14 @@ using Ryujinx.Horizon.Sdk.Sf; using Ryujinx.Horizon.Sdk.Sm; using Ryujinx.Horizon.Sm.Impl; -namespace Ryujinx.Horizon.Sm +namespace Ryujinx.Horizon.Sm.Ipc { - partial class UserService : IServiceObject + partial class UserService : IUserService { private readonly ServiceManager _serviceManager; private ulong _clientProcessId; - private bool _initialized; + private bool _initialized; public UserService(ServiceManager serviceManager) { @@ -21,7 +21,7 @@ namespace Ryujinx.Horizon.Sm public Result Initialize([ClientProcessId] ulong clientProcessId) { _clientProcessId = clientProcessId; - _initialized = true; + _initialized = true; return Result.Success; } @@ -63,4 +63,4 @@ namespace Ryujinx.Horizon.Sm return _serviceManager.UnregisterService(_clientProcessId, name); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sm/ManagerService.cs b/Ryujinx.Horizon/Sm/ManagerService.cs deleted file mode 100644 index 1719dcfd5..000000000 --- a/Ryujinx.Horizon/Sm/ManagerService.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Ryujinx.Horizon.Sdk.Sf; - -namespace Ryujinx.Horizon.Sm -{ - partial class ManagerService : IServiceObject - { - } -} diff --git a/Ryujinx.Horizon/Sm/SmMain.cs b/Ryujinx.Horizon/Sm/SmMain.cs index 8c37bece5..5656d464f 100644 --- a/Ryujinx.Horizon/Sm/SmMain.cs +++ b/Ryujinx.Horizon/Sm/SmMain.cs @@ -1,30 +1,34 @@ -using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Prepo.Types; +using Ryujinx.Horizon.Prepo; +using Ryujinx.Horizon.Sdk.Sf.Hipc; using Ryujinx.Horizon.Sdk.Sm; using Ryujinx.Horizon.Sm.Impl; +using Ryujinx.Horizon.Sm.Types; namespace Ryujinx.Horizon.Sm { public class SmMain { - private enum PortIndex - { - User, - Manager - } + private const int SmMaxSessionsCount = 64; + private const int SmmMaxSessionsCount = 1; + private const int SmTotalMaxSessionsCount = SmMaxSessionsCount + SmmMaxSessionsCount; private const int MaxPortsCount = 2; - private readonly ServerManager _serverManager = new ServerManager(null, null, MaxPortsCount, ManagerOptions.Default, 0); - private readonly ServiceManager _serviceManager = new ServiceManager(); + private SmServerManager _serverManager; + + private readonly ServiceManager _serviceManager = new(); public void Main() { - HorizonStatic.Syscall.ManageNamedPort(out int smHandle, "sm:", 64).AbortOnFailure(); + HorizonStatic.Syscall.ManageNamedPort(out int smHandle, "sm:", SmMaxSessionsCount).AbortOnFailure(); - _serverManager.RegisterServer((int)PortIndex.User, smHandle); - _serviceManager.RegisterServiceForSelf(out int smmHandle, ServiceName.Encode("sm:m"), 1).AbortOnFailure(); - _serverManager.RegisterServer((int)PortIndex.Manager, smmHandle); + _serverManager = new SmServerManager(_serviceManager, null, null, MaxPortsCount, ManagerOptions.Default, SmTotalMaxSessionsCount); + + _serverManager.RegisterServer((int)SmPortIndex.User, smHandle); + _serviceManager.RegisterServiceForSelf(out int smmHandle, ServiceName.Encode("sm:m"), SmmMaxSessionsCount).AbortOnFailure(); + _serverManager.RegisterServer((int)SmPortIndex.Manager, smmHandle); _serverManager.ServiceRequests(); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sm/SmResult.cs b/Ryujinx.Horizon/Sm/SmResult.cs index 3063445dc..2d503a4f8 100644 --- a/Ryujinx.Horizon/Sm/SmResult.cs +++ b/Ryujinx.Horizon/Sm/SmResult.cs @@ -6,14 +6,14 @@ namespace Ryujinx.Horizon.Sm { private const int ModuleId = 21; - public static Result OutOfProcess => new Result(ModuleId, 1); - public static Result InvalidClient => new Result(ModuleId, 2); - public static Result OutOfSessions => new Result(ModuleId, 3); - public static Result AlreadyRegistered => new Result(ModuleId, 4); - public static Result OutOfServices => new Result(ModuleId, 5); - public static Result InvalidServiceName => new Result(ModuleId, 6); - public static Result NotRegistered => new Result(ModuleId, 7); - public static Result NotAllowed => new Result(ModuleId, 8); - public static Result TooLargeAccessControl => new Result(ModuleId, 9); + public static Result OutOfProcess => new(ModuleId, 1); + public static Result InvalidClient => new(ModuleId, 2); + public static Result OutOfSessions => new(ModuleId, 3); + public static Result AlreadyRegistered => new(ModuleId, 4); + public static Result OutOfServices => new(ModuleId, 5); + public static Result InvalidServiceName => new(ModuleId, 6); + public static Result NotRegistered => new(ModuleId, 7); + public static Result NotAllowed => new(ModuleId, 8); + public static Result TooLargeAccessControl => new(ModuleId, 9); } -} +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sm/SmServerManager.cs b/Ryujinx.Horizon/Sm/SmServerManager.cs new file mode 100644 index 000000000..dc8dc5b67 --- /dev/null +++ b/Ryujinx.Horizon/Sm/SmServerManager.cs @@ -0,0 +1,30 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; +using Ryujinx.Horizon.Sm.Impl; +using Ryujinx.Horizon.Sm.Ipc; +using Ryujinx.Horizon.Sm.Types; +using System; + +namespace Ryujinx.Horizon.Sm +{ + class SmServerManager : ServerManager + { + private readonly ServiceManager _serviceManager; + + public SmServerManager(ServiceManager serviceManager, HeapAllocator allocator, SmApi sm, int maxPorts, ManagerOptions options, int maxSessions) : base(allocator, sm, maxPorts, options, maxSessions) + { + _serviceManager = serviceManager; + } + + protected override Result OnNeedsToAccept(int portIndex, Server server) + { + return (SmPortIndex)portIndex switch + { + SmPortIndex.User => AcceptImpl(server, new UserService(_serviceManager)), + SmPortIndex.Manager => AcceptImpl(server, new ManagerService()), + _ => throw new ArgumentOutOfRangeException(nameof(portIndex)), + }; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs b/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs new file mode 100644 index 000000000..5325558b8 --- /dev/null +++ b/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Horizon.Sm.Types +{ + enum SmPortIndex + { + User, + Manager + } +} \ No newline at end of file