forked from Mirror/Ryujinx
Horizon: Impl Prepo, Fixes bugs, Clean things (#4220)
* Horizon: Impl Prepo, Fixes bugs, Clean things * remove ToArray() * resultCode > status * Remove old services * Addresses gdkchan's comments and more cleanup * Addresses Gdkchan's feedback 2 * Reorganize services, make sure service are loaded before guest Co-Authored-By: gdkchan <5624669+gdkchan@users.noreply.github.com> * Create interfaces for lm and sm Co-authored-by: gdkchan <5624669+gdkchan@users.noreply.github.com>
This commit is contained in:
parent
3ffceab1fb
commit
550747eac6
83 changed files with 1106 additions and 880 deletions
|
@ -123,6 +123,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
internal LibHacHorizonManager LibHacHorizonManager { get; private set; }
|
internal LibHacHorizonManager LibHacHorizonManager { get; private set; }
|
||||||
|
|
||||||
|
internal ServiceTable ServiceTable { get; private set; }
|
||||||
|
|
||||||
public bool IsPaused { get; private set; }
|
public bool IsPaused { get; private set; }
|
||||||
|
|
||||||
public Horizon(Switch device)
|
public Horizon(Switch device)
|
||||||
|
@ -326,6 +328,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
private void StartNewServices()
|
private void StartNewServices()
|
||||||
{
|
{
|
||||||
|
ServiceTable = new ServiceTable();
|
||||||
var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices));
|
var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices));
|
||||||
|
|
||||||
foreach (var service in services)
|
foreach (var service in services)
|
||||||
|
|
|
@ -182,6 +182,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
byte[] arguments = null,
|
byte[] arguments = null,
|
||||||
params IExecutable[] executables)
|
params IExecutable[] executables)
|
||||||
{
|
{
|
||||||
|
context.Device.System.ServiceTable.WaitServicesReady();
|
||||||
|
|
||||||
LibHac.Result rc = metaData.GetNpdm(out var npdm);
|
LibHac.Result rc = metaData.GetNpdm(out var npdm);
|
||||||
|
|
||||||
if (rc.IsFailure())
|
if (rc.IsFailure())
|
||||||
|
|
|
@ -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<nn::lm::ILogger>
|
|
||||||
public ResultCode Initialize(ServiceCtx context)
|
|
||||||
{
|
|
||||||
MakeObject(context, new ILogger());
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<unknown, 0x21>)
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Lm.LogService
|
|
||||||
{
|
|
||||||
enum LmLogLevel
|
|
||||||
{
|
|
||||||
Trace,
|
|
||||||
Info,
|
|
||||||
Warning,
|
|
||||||
Error,
|
|
||||||
Critical
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<u8, 9>, buffer<bytes, 5>)
|
|
||||||
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<u8, 9>, buffer<bytes, 5>)
|
|
||||||
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<u8, 9>, buffer<bytes, 5>)
|
|
||||||
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<u8, 9>, buffer<bytes, 5>)
|
|
||||||
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<UserId>() : 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -180,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
||||||
return ResultCode.InvalidName;
|
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);
|
KPort port = new KPort(context.Device.System.KernelContext, maxSessions, isLight, null);
|
||||||
|
|
||||||
|
|
|
@ -123,44 +123,51 @@ namespace Ryujinx.Horizon.Generators.Hipc
|
||||||
{
|
{
|
||||||
string[] args = new string[method.ParameterList.Parameters.Count];
|
string[] args = new string[method.ParameterList.Parameters.Count];
|
||||||
|
|
||||||
int index = 0;
|
if (args.Length == 0)
|
||||||
|
|
||||||
foreach (var parameter in method.ParameterList.Parameters)
|
|
||||||
{
|
{
|
||||||
string canonicalTypeName = GetCanonicalTypeNameWithGenericArguments(compilation, parameter.Type);
|
generator.AppendLine($"{{ {commandId}, new CommandHandler({method.Identifier.Text}, Array.Empty<CommandArg>()) }},");
|
||||||
CommandArgType argType = GetCommandArgType(compilation, parameter);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
string arg;
|
foreach (var parameter in method.ParameterList.Parameters)
|
||||||
|
|
||||||
if (argType == CommandArgType.Buffer)
|
|
||||||
{
|
{
|
||||||
string bufferFlags = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 0);
|
string canonicalTypeName = GetCanonicalTypeNameWithGenericArguments(compilation, parameter.Type);
|
||||||
string bufferFixedSize = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 1);
|
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
|
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})";
|
args[index++] = arg;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
arg = $"new CommandArg(CommandArgType.{argType})";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)}) }},");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,12 +12,12 @@ namespace Ryujinx.Horizon
|
||||||
private struct Range : IComparable<Range>
|
private struct Range : IComparable<Range>
|
||||||
{
|
{
|
||||||
public ulong Offset { get; }
|
public ulong Offset { get; }
|
||||||
public ulong Size { get; }
|
public ulong Size { get; }
|
||||||
|
|
||||||
public Range(ulong offset, ulong size)
|
public Range(ulong offset, ulong size)
|
||||||
{
|
{
|
||||||
Offset = offset;
|
Offset = offset;
|
||||||
Size = size;
|
Size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int CompareTo(Range other)
|
public int CompareTo(Range other)
|
||||||
|
@ -31,7 +31,7 @@ namespace Ryujinx.Horizon
|
||||||
|
|
||||||
public HeapAllocator()
|
public HeapAllocator()
|
||||||
{
|
{
|
||||||
_freeRanges = new List<Range>();
|
_freeRanges = new List<Range>();
|
||||||
_currentHeapSize = 0;
|
_currentHeapSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,8 +70,8 @@ namespace Ryujinx.Horizon
|
||||||
var range = _freeRanges[i];
|
var range = _freeRanges[i];
|
||||||
|
|
||||||
ulong alignedOffset = BitUtils.AlignUp(range.Offset, alignment);
|
ulong alignedOffset = BitUtils.AlignUp(range.Offset, alignment);
|
||||||
ulong sizeDelta = alignedOffset - range.Offset;
|
ulong sizeDelta = alignedOffset - range.Offset;
|
||||||
ulong usableSize = range.Size - sizeDelta;
|
ulong usableSize = range.Size - sizeDelta;
|
||||||
|
|
||||||
if (sizeDelta < range.Size && usableSize >= size)
|
if (sizeDelta < range.Size && usableSize >= size)
|
||||||
{
|
{
|
||||||
|
@ -82,7 +82,7 @@ namespace Ryujinx.Horizon
|
||||||
InsertFreeRange(range.Offset, sizeDelta);
|
InsertFreeRange(range.Offset, sizeDelta);
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong endOffset = range.Offset + range.Size;
|
ulong endOffset = range.Offset + range.Size;
|
||||||
ulong remainingSize = endOffset - (alignedOffset + size);
|
ulong remainingSize = endOffset - (alignedOffset + size);
|
||||||
if (remainingSize != 0)
|
if (remainingSize != 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,11 +2,13 @@ namespace Ryujinx.Horizon
|
||||||
{
|
{
|
||||||
public struct HorizonOptions
|
public struct HorizonOptions
|
||||||
{
|
{
|
||||||
public bool IgnoreMissingServices { get; }
|
public bool IgnoreMissingServices { get; }
|
||||||
|
public bool ThrowOnInvalidCommandIds { get; }
|
||||||
|
|
||||||
public HorizonOptions(bool ignoreMissingServices)
|
public HorizonOptions(bool ignoreMissingServices)
|
||||||
{
|
{
|
||||||
IgnoreMissingServices = ignoreMissingServices;
|
IgnoreMissingServices = ignoreMissingServices;
|
||||||
|
ThrowOnInvalidCommandIds = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,24 +21,24 @@ namespace Ryujinx.Horizon
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static int _threadHandle;
|
private static int _threadHandle;
|
||||||
|
|
||||||
public static HorizonOptions Options => _options;
|
public static HorizonOptions Options => _options;
|
||||||
public static ISyscallApi Syscall => _syscall;
|
public static ISyscallApi Syscall => _syscall;
|
||||||
public static IVirtualMemoryManager AddressSpace => _addressSpace;
|
public static IVirtualMemoryManager AddressSpace => _addressSpace;
|
||||||
public static IThreadContext ThreadContext => _threadContext;
|
public static IThreadContext ThreadContext => _threadContext;
|
||||||
public static int CurrentThreadHandle => _threadHandle;
|
public static int CurrentThreadHandle => _threadHandle;
|
||||||
|
|
||||||
public static void Register(
|
public static void Register(
|
||||||
HorizonOptions options,
|
HorizonOptions options,
|
||||||
ISyscallApi syscallApi,
|
ISyscallApi syscallApi,
|
||||||
IVirtualMemoryManager addressSpace,
|
IVirtualMemoryManager addressSpace,
|
||||||
IThreadContext threadContext,
|
IThreadContext threadContext,
|
||||||
int threadHandle)
|
int threadHandle)
|
||||||
{
|
{
|
||||||
_options = options;
|
_options = options;
|
||||||
_syscall = syscallApi;
|
_syscall = syscallApi;
|
||||||
_addressSpace = addressSpace;
|
_addressSpace = addressSpace;
|
||||||
_threadContext = threadContext;
|
_threadContext = threadContext;
|
||||||
_threadHandle = threadHandle;
|
_threadHandle = threadHandle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
{
|
{
|
||||||
interface IService
|
interface IService
|
||||||
{
|
{
|
||||||
abstract static void Main();
|
abstract static void Main(ServiceTable serviceTable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,23 +9,23 @@ using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
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 LogService _log;
|
||||||
private readonly ulong _clientProcessId;
|
private readonly ulong _pid;
|
||||||
|
|
||||||
public LmLogger(LmLog log, ulong clientProcessId)
|
public LmLogger(LogService log, ulong pid)
|
||||||
{
|
{
|
||||||
_log = log;
|
_log = log;
|
||||||
_clientProcessId = clientProcessId;
|
_pid = pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
[CmifCommand(0)]
|
[CmifCommand(0)]
|
||||||
public Result Log([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] Span<byte> message)
|
public Result Log([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] Span<byte> message)
|
||||||
{
|
{
|
||||||
if (!SetProcessId(message, _clientProcessId))
|
if (!SetProcessId(message, _pid))
|
||||||
{
|
{
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ namespace Ryujinx.Horizon.LogManager
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[CmifCommand(1)]
|
[CmifCommand(1)] // 3.0.0+
|
||||||
public Result SetDestination(LogDestination destination)
|
public Result SetDestination(LogDestination destination)
|
||||||
{
|
{
|
||||||
_log.LogDestination = destination;
|
_log.LogDestination = destination;
|
||||||
|
@ -48,7 +48,6 @@ namespace Ryujinx.Horizon.LogManager
|
||||||
ref LogPacketHeader header = ref MemoryMarshal.Cast<byte, LogPacketHeader>(message)[0];
|
ref LogPacketHeader header = ref MemoryMarshal.Cast<byte, LogPacketHeader>(message)[0];
|
||||||
|
|
||||||
uint expectedMessageSize = (uint)Unsafe.SizeOf<LogPacketHeader>() + header.PayloadSize;
|
uint expectedMessageSize = (uint)Unsafe.SizeOf<LogPacketHeader>() + header.PayloadSize;
|
||||||
|
|
||||||
if (expectedMessageSize != (uint)message.Length)
|
if (expectedMessageSize != (uint)message.Length)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.ServiceLm, $"Invalid message size (expected 0x{expectedMessageSize:X} but got 0x{message.Length:X}).");
|
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<byte> message)
|
private static string LogImpl(ReadOnlySpan<byte> message)
|
||||||
{
|
{
|
||||||
SpanReader reader = new SpanReader(message);
|
SpanReader reader = new(message);
|
||||||
|
LogPacketHeader header = reader.Read<LogPacketHeader>();
|
||||||
|
StringBuilder builder = new();
|
||||||
|
|
||||||
LogPacketHeader header = reader.Read<LogPacketHeader>();
|
builder.AppendLine($"Guest Log:\n Log level: {header.Severity}");
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
|
|
||||||
sb.AppendLine($"Guest Log:\n Log level: {header.Severity}");
|
|
||||||
|
|
||||||
while (reader.Length > 0)
|
while (reader.Length > 0)
|
||||||
{
|
{
|
||||||
|
@ -78,7 +75,7 @@ namespace Ryujinx.Horizon.LogManager
|
||||||
|
|
||||||
LogDataChunkKey field = (LogDataChunkKey)type;
|
LogDataChunkKey field = (LogDataChunkKey)type;
|
||||||
|
|
||||||
string fieldStr = string.Empty;
|
string fieldStr;
|
||||||
|
|
||||||
if (field == LogDataChunkKey.Start)
|
if (field == LogDataChunkKey.Start)
|
||||||
{
|
{
|
||||||
|
@ -111,16 +108,16 @@ namespace Ryujinx.Horizon.LogManager
|
||||||
fieldStr = $"Field{field}: '{Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd()}'";
|
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)
|
private static int ReadUleb128(ref SpanReader reader)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
byte encoded;
|
byte encoded;
|
||||||
|
|
||||||
|
@ -136,4 +133,4 @@ namespace Ryujinx.Horizon.LogManager
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,18 +2,19 @@
|
||||||
using Ryujinx.Horizon.Sdk.Lm;
|
using Ryujinx.Horizon.Sdk.Lm;
|
||||||
using Ryujinx.Horizon.Sdk.Sf;
|
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;
|
public LogDestination LogDestination { get; set; } = LogDestination.TargetManager;
|
||||||
|
|
||||||
[CmifCommand(0)]
|
[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;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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.Sdk.Sm;
|
||||||
using Ryujinx.Horizon.Sm;
|
|
||||||
|
|
||||||
namespace Ryujinx.Horizon.LogManager
|
namespace Ryujinx.Horizon.LogManager
|
||||||
{
|
{
|
||||||
|
@ -9,36 +9,25 @@ namespace Ryujinx.Horizon.LogManager
|
||||||
private const int LogMaxSessionsCount = 42;
|
private const int LogMaxSessionsCount = 42;
|
||||||
|
|
||||||
private const int PointerBufferSize = 0x400;
|
private const int PointerBufferSize = 0x400;
|
||||||
private const int MaxDomains = 31;
|
private const int MaxDomains = 31;
|
||||||
private const int MaxDomainObjects = 61;
|
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(
|
private SmApi _sm;
|
||||||
PointerBufferSize,
|
|
||||||
MaxDomains,
|
|
||||||
MaxDomainObjects,
|
|
||||||
false);
|
|
||||||
|
|
||||||
private static readonly ServiceName _logServiceName = ServiceName.Encode("lm");
|
|
||||||
|
|
||||||
private SmApi _sm;
|
|
||||||
private ServerManager _serverManager;
|
private ServerManager _serverManager;
|
||||||
|
|
||||||
private LmLog _logServiceObject;
|
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
HeapAllocator allocator = new HeapAllocator();
|
HeapAllocator allocator = new();
|
||||||
|
|
||||||
_sm = new SmApi();
|
_sm = new SmApi();
|
||||||
_sm.Initialize().AbortOnFailure();
|
_sm.Initialize().AbortOnFailure();
|
||||||
|
|
||||||
_serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, LogMaxSessionsCount);
|
_serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, LogMaxSessionsCount);
|
||||||
|
|
||||||
_logServiceObject = new LmLog();
|
_serverManager.RegisterObjectForServer(new LogService(), ServiceName.Encode("lm"), LogMaxSessionsCount);
|
||||||
|
|
||||||
_serverManager.RegisterObjectForServer(_logServiceObject, _logServiceName, LogMaxSessionsCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ServiceRequests()
|
public void ServiceRequests()
|
||||||
|
@ -51,4 +40,4 @@ namespace Ryujinx.Horizon.LogManager
|
||||||
_serverManager.Dispose();
|
_serverManager.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,13 +2,16 @@
|
||||||
{
|
{
|
||||||
class LmMain : IService
|
class LmMain : IService
|
||||||
{
|
{
|
||||||
public static void Main()
|
public static void Main(ServiceTable serviceTable)
|
||||||
{
|
{
|
||||||
LmIpcServer ipcServer = new LmIpcServer();
|
LmIpcServer ipcServer = new();
|
||||||
|
|
||||||
ipcServer.Initialize();
|
ipcServer.Initialize();
|
||||||
|
|
||||||
|
serviceTable.SignalServiceReady();
|
||||||
|
|
||||||
ipcServer.ServiceRequests();
|
ipcServer.ServiceRequests();
|
||||||
ipcServer.Shutdown();
|
ipcServer.Shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
218
Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs
Normal file
218
Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs
Normal file
|
@ -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<byte> gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> 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<byte> gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> 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<byte> gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> 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<byte> gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> 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<byte> gameRoomBuffer, ReadOnlySpan<byte> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
Ryujinx.Horizon/Prepo/PrepoIpcServer.cs
Normal file
49
Ryujinx.Horizon/Prepo/PrepoIpcServer.cs
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
Ryujinx.Horizon/Prepo/PrepoMain.cs
Normal file
17
Ryujinx.Horizon/Prepo/PrepoMain.cs
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
Ryujinx.Horizon/Prepo/PrepoResult.cs
Normal file
15
Ryujinx.Horizon/Prepo/PrepoResult.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
30
Ryujinx.Horizon/Prepo/PrepoServerManager.cs
Normal file
30
Ryujinx.Horizon/Prepo/PrepoServerManager.cs
Normal file
|
@ -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)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs
Normal file
12
Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Ryujinx.Horizon.Prepo.Types
|
||||||
|
{
|
||||||
|
enum PrepoPortIndex
|
||||||
|
{
|
||||||
|
Admin,
|
||||||
|
Admin2,
|
||||||
|
Manager,
|
||||||
|
User,
|
||||||
|
System,
|
||||||
|
Debug
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,11 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Prepo
|
namespace Ryujinx.Horizon.Prepo.Types
|
||||||
{
|
{
|
||||||
enum PrepoServicePermissionLevel
|
enum PrepoServicePermissionLevel
|
||||||
{
|
{
|
||||||
Admin = -1,
|
Admin = -1,
|
||||||
User = 1,
|
User = 1,
|
||||||
System = 2,
|
System = 2,
|
||||||
Manager = 6
|
Manager = 6,
|
||||||
|
Debug = unchecked((int)0x80000006)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,4 +11,8 @@
|
||||||
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="LibHac" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
62
Ryujinx.Horizon/Sdk/Account/Uid.cs
Normal file
62
Ryujinx.Horizon/Sdk/Account/Uid.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs
Normal file
12
Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs
Normal file
|
@ -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<byte> message);
|
||||||
|
Result SetDestination(LogDestination destination);
|
||||||
|
}
|
||||||
|
}
|
11
Ryujinx.Horizon/Sdk/Lm/ILogService.cs
Normal file
11
Ryujinx.Horizon/Sdk/Lm/ILogService.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
20
Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs
Normal file
20
Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs
Normal file
|
@ -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<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid);
|
||||||
|
Result SaveReportWithUser(Uid userId, ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid);
|
||||||
|
Result RequestImmediateTransmission();
|
||||||
|
Result GetTransmissionStatus(out int status);
|
||||||
|
Result GetSystemSessionId(out ulong systemSessionId);
|
||||||
|
Result SaveSystemReport(ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid);
|
||||||
|
Result SaveSystemReportWithUser(Uid userId, ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid);
|
||||||
|
Result IsUserAgreementCheckEnabled(out bool enabled);
|
||||||
|
Result SetUserAgreementCheckEnabled(bool enabled);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,15 +10,15 @@ namespace Ryujinx.Horizon.Sdk
|
||||||
public static Result SendRequest(out CmifResponse response, int sessionHandle, uint requestId, bool sendPid, scoped ReadOnlySpan<byte> data)
|
public static Result SendRequest(out CmifResponse response, int sessionHandle, uint requestId, bool sendPid, scoped ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
ulong tlsAddress = HorizonStatic.ThreadContext.TlsAddress;
|
ulong tlsAddress = HorizonStatic.ThreadContext.TlsAddress;
|
||||||
int tlsSize = Api.TlsMessageBufferSize;
|
int tlsSize = Api.TlsMessageBufferSize;
|
||||||
|
|
||||||
using (var tlsRegion = HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize))
|
using (var tlsRegion = HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize))
|
||||||
{
|
{
|
||||||
CmifRequest request = CmifMessage.CreateRequest(tlsRegion.Memory.Span, new CmifRequestFormat()
|
CmifRequest request = CmifMessage.CreateRequest(tlsRegion.Memory.Span, new CmifRequestFormat()
|
||||||
{
|
{
|
||||||
DataSize = data.Length,
|
DataSize = data.Length,
|
||||||
RequestId = requestId,
|
RequestId = requestId,
|
||||||
SendPid = sendPid
|
SendPid = sendPid
|
||||||
});
|
});
|
||||||
|
|
||||||
data.CopyTo(request.Data);
|
data.CopyTo(request.Data);
|
||||||
|
@ -29,10 +29,11 @@ namespace Ryujinx.Horizon.Sdk
|
||||||
if (result.IsFailure)
|
if (result.IsFailure)
|
||||||
{
|
{
|
||||||
response = default;
|
response = default;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CmifMessage.ParseResponse(out response, HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize).Memory.Span, false, 0);
|
return CmifMessage.ParseResponse(out response, HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize).Memory.Span, false, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,10 +3,10 @@
|
||||||
struct CmifDomainInHeader
|
struct CmifDomainInHeader
|
||||||
{
|
{
|
||||||
public CmifDomainRequestType Type;
|
public CmifDomainRequestType Type;
|
||||||
public byte ObjectsCount;
|
public byte ObjectsCount;
|
||||||
public ushort DataSize;
|
public ushort DataSize;
|
||||||
public int ObjectId;
|
public int ObjectId;
|
||||||
public uint Padding;
|
public uint Padding;
|
||||||
public uint Token;
|
public uint Token;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
{
|
{
|
||||||
enum CmifDomainRequestType : byte
|
enum CmifDomainRequestType : byte
|
||||||
{
|
{
|
||||||
Invalid = 0,
|
Invalid = 0,
|
||||||
SendMessage = 1,
|
SendMessage = 1,
|
||||||
Close = 2
|
Close = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
{
|
{
|
||||||
static class CmifMessage
|
static class CmifMessage
|
||||||
{
|
{
|
||||||
public const uint CmifInHeaderMagic = 0x49434653; // SFCI
|
public const uint CmifInHeaderMagic = 0x49434653; // SFCI
|
||||||
public const uint CmifOutHeaderMagic = 0x4f434653; // SFCO
|
public const uint CmifOutHeaderMagic = 0x4f434653; // SFCO
|
||||||
|
|
||||||
public static CmifRequest CreateRequest(Span<byte> output, CmifRequestFormat format)
|
public static CmifRequest CreateRequest(Span<byte> output, CmifRequestFormat format)
|
||||||
|
@ -21,27 +21,31 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
}
|
}
|
||||||
|
|
||||||
totalSize += Unsafe.SizeOf<CmifInHeader>() + format.DataSize;
|
totalSize += Unsafe.SizeOf<CmifInHeader>() + format.DataSize;
|
||||||
totalSize = (totalSize + 1) & ~1;
|
totalSize = (totalSize + 1) & ~1;
|
||||||
|
|
||||||
int outPointerSizeTableOffset = totalSize;
|
int outPointerSizeTableOffset = totalSize;
|
||||||
int outPointerSizeTableSize = format.OutAutoBuffersCount + format.OutPointersCount;
|
int outPointerSizeTableSize = format.OutAutoBuffersCount + format.OutPointersCount;
|
||||||
|
|
||||||
totalSize += sizeof(ushort) * outPointerSizeTableSize;
|
totalSize += sizeof(ushort) * outPointerSizeTableSize;
|
||||||
|
|
||||||
int rawDataSizeInWords = (totalSize + sizeof(uint) - 1) / sizeof(uint);
|
int rawDataSizeInWords = (totalSize + sizeof(uint) - 1) / sizeof(uint);
|
||||||
|
|
||||||
CmifRequest request = new CmifRequest();
|
CmifRequest request = new()
|
||||||
|
|
||||||
request.Hipc = HipcMessage.WriteMessage(output, new HipcMetadata()
|
|
||||||
{
|
{
|
||||||
Type = format.Context != 0 ? (int)CommandType.RequestWithContext : (int)CommandType.Request,
|
Hipc = HipcMessage.WriteMessage(output, new HipcMetadata()
|
||||||
SendStaticsCount = format.InAutoBuffersCount + format.InPointersCount,
|
{
|
||||||
SendBuffersCount = format.InAutoBuffersCount + format.InBuffersCount,
|
Type = format.Context != 0 ? (int)CommandType.RequestWithContext : (int)CommandType.Request,
|
||||||
ReceiveBuffersCount = format.OutAutoBuffersCount + format.OutBuffersCount,
|
SendStaticsCount = format.InAutoBuffersCount + format.InPointersCount,
|
||||||
ExchangeBuffersCount = format.InOutBuffersCount,
|
SendBuffersCount = format.InAutoBuffersCount + format.InBuffersCount,
|
||||||
DataWordsCount = rawDataSizeInWords,
|
ReceiveBuffersCount = format.OutAutoBuffersCount + format.OutBuffersCount,
|
||||||
ReceiveStaticsCount = outPointerSizeTableSize + format.OutFixedPointersCount,
|
ExchangeBuffersCount = format.InOutBuffersCount,
|
||||||
SendPid = format.SendPid,
|
DataWordsCount = rawDataSizeInWords,
|
||||||
CopyHandlesCount = format.HandlesCount,
|
ReceiveStaticsCount = outPointerSizeTableSize + format.OutFixedPointersCount,
|
||||||
MoveHandlesCount = 0
|
SendPid = format.SendPid,
|
||||||
});
|
CopyHandlesCount = format.HandlesCount,
|
||||||
|
MoveHandlesCount = 0
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
Span<uint> data = request.Hipc.DataWords;
|
Span<uint> data = request.Hipc.DataWords;
|
||||||
|
|
||||||
|
@ -53,35 +57,36 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
|
|
||||||
domainHeader = new CmifDomainInHeader()
|
domainHeader = new CmifDomainInHeader()
|
||||||
{
|
{
|
||||||
Type = CmifDomainRequestType.SendMessage,
|
Type = CmifDomainRequestType.SendMessage,
|
||||||
ObjectsCount = (byte)format.ObjectsCount,
|
ObjectsCount = (byte)format.ObjectsCount,
|
||||||
DataSize = (ushort)payloadSize,
|
DataSize = (ushort)payloadSize,
|
||||||
ObjectId = format.ObjectId,
|
ObjectId = format.ObjectId,
|
||||||
Padding = 0,
|
Padding = 0,
|
||||||
Token = format.Context
|
Token = format.Context
|
||||||
};
|
};
|
||||||
|
|
||||||
data = data.Slice(Unsafe.SizeOf<CmifDomainInHeader>() / sizeof(uint));
|
data = data[(Unsafe.SizeOf<CmifDomainInHeader>() / 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<uint, CmifInHeader>(data)[0];
|
ref CmifInHeader header = ref MemoryMarshal.Cast<uint, CmifInHeader>(data)[0];
|
||||||
|
|
||||||
header = new CmifInHeader()
|
header = new CmifInHeader()
|
||||||
{
|
{
|
||||||
Magic = CmifInHeaderMagic,
|
Magic = CmifInHeaderMagic,
|
||||||
Version = format.Context != 0 ? 1u : 0u,
|
Version = format.Context != 0 ? 1u : 0u,
|
||||||
CommandId = format.RequestId,
|
CommandId = format.RequestId,
|
||||||
Token = format.ObjectId != 0 ? 0u : format.Context
|
Token = format.ObjectId != 0 ? 0u : format.Context
|
||||||
};
|
};
|
||||||
|
|
||||||
request.Data = MemoryMarshal.Cast<uint, byte>(data).Slice(Unsafe.SizeOf<CmifInHeader>());
|
request.Data = MemoryMarshal.Cast<uint, byte>(data)[Unsafe.SizeOf<CmifInHeader>()..];
|
||||||
|
|
||||||
int paddingSizeBefore = (rawDataSizeInWords - request.Hipc.DataWords.Length) * sizeof(uint);
|
int paddingSizeBefore = (rawDataSizeInWords - request.Hipc.DataWords.Length) * sizeof(uint);
|
||||||
|
|
||||||
Span<byte> outPointerTable = MemoryMarshal.Cast<uint, byte>(request.Hipc.DataWords).Slice(outPointerSizeTableOffset - paddingSizeBefore);
|
Span<byte> outPointerTable = MemoryMarshal.Cast<uint, byte>(request.Hipc.DataWords)[(outPointerSizeTableOffset - paddingSizeBefore)..];
|
||||||
request.OutPointerSizes = MemoryMarshal.Cast<byte, ushort>(outPointerTable);
|
|
||||||
|
request.OutPointerSizes = MemoryMarshal.Cast<byte, ushort>(outPointerTable);
|
||||||
request.ServerPointerSize = format.ServerPointerSize;
|
request.ServerPointerSize = format.ServerPointerSize;
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
|
@ -89,15 +94,15 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
|
|
||||||
public static Result ParseResponse(out CmifResponse response, Span<byte> input, bool isDomain, int size)
|
public static Result ParseResponse(out CmifResponse response, Span<byte> input, bool isDomain, int size)
|
||||||
{
|
{
|
||||||
HipcMessage responseMessage = new HipcMessage(input);
|
HipcMessage responseMessage = new(input);
|
||||||
|
|
||||||
Span<byte> data = MemoryMarshal.Cast<uint, byte>(responseMessage.Data.DataWords);
|
Span<byte> data = MemoryMarshal.Cast<uint, byte>(responseMessage.Data.DataWords);
|
||||||
Span<uint> objects = Span<uint>.Empty;
|
Span<uint> objects = Span<uint>.Empty;
|
||||||
|
|
||||||
if (isDomain)
|
if (isDomain)
|
||||||
{
|
{
|
||||||
data = data.Slice(Unsafe.SizeOf<CmifDomainOutHeader>());
|
data = data[Unsafe.SizeOf<CmifDomainOutHeader>()..];
|
||||||
objects = MemoryMarshal.Cast<byte, uint>(data.Slice(Unsafe.SizeOf<CmifOutHeader>() + size));
|
objects = MemoryMarshal.Cast<byte, uint>(data[(Unsafe.SizeOf<CmifOutHeader>() + size)..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
CmifOutHeader header = MemoryMarshal.Cast<byte, CmifOutHeader>(data)[0];
|
CmifOutHeader header = MemoryMarshal.Cast<byte, CmifOutHeader>(data)[0];
|
||||||
|
@ -105,19 +110,21 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
if (header.Magic != CmifOutHeaderMagic)
|
if (header.Magic != CmifOutHeaderMagic)
|
||||||
{
|
{
|
||||||
response = default;
|
response = default;
|
||||||
|
|
||||||
return SfResult.InvalidOutHeader;
|
return SfResult.InvalidOutHeader;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.Result.IsFailure)
|
if (header.Result.IsFailure)
|
||||||
{
|
{
|
||||||
response = default;
|
response = default;
|
||||||
|
|
||||||
return header.Result;
|
return header.Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
response = new CmifResponse()
|
response = new CmifResponse()
|
||||||
{
|
{
|
||||||
Data = data.Slice(Unsafe.SizeOf<CmifOutHeader>()),
|
Data = data[Unsafe.SizeOf<CmifOutHeader>()..],
|
||||||
Objects = objects,
|
Objects = objects,
|
||||||
CopyHandles = responseMessage.Data.CopyHandles,
|
CopyHandles = responseMessage.Data.CopyHandles,
|
||||||
MoveHandles = responseMessage.Data.MoveHandles
|
MoveHandles = responseMessage.Data.MoveHandles
|
||||||
};
|
};
|
||||||
|
@ -125,4 +132,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,10 +5,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
struct CmifOutHeader
|
struct CmifOutHeader
|
||||||
{
|
{
|
||||||
#pragma warning disable CS0649
|
#pragma warning disable CS0649
|
||||||
public uint Magic;
|
public uint Magic;
|
||||||
public uint Version;
|
public uint Version;
|
||||||
public Result Result;
|
public Result Result;
|
||||||
public uint Token;
|
public uint Token;
|
||||||
#pragma warning restore CS0649
|
#pragma warning restore CS0649
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
ref struct CmifRequest
|
ref struct CmifRequest
|
||||||
{
|
{
|
||||||
public HipcMessageData Hipc;
|
public HipcMessageData Hipc;
|
||||||
public Span<byte> Data;
|
public Span<byte> Data;
|
||||||
public Span<ushort> OutPointerSizes;
|
public Span<ushort> OutPointerSizes;
|
||||||
public Span<uint> Objects;
|
public Span<uint> Objects;
|
||||||
public int ServerPointerSize;
|
public int ServerPointerSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,21 +3,21 @@
|
||||||
struct CmifRequestFormat
|
struct CmifRequestFormat
|
||||||
{
|
{
|
||||||
#pragma warning disable CS0649
|
#pragma warning disable CS0649
|
||||||
public int ObjectId;
|
public int ObjectId;
|
||||||
public uint RequestId;
|
public uint RequestId;
|
||||||
public uint Context;
|
public uint Context;
|
||||||
public int DataSize;
|
public int DataSize;
|
||||||
public int ServerPointerSize;
|
public int ServerPointerSize;
|
||||||
public int InAutoBuffersCount;
|
public int InAutoBuffersCount;
|
||||||
public int OutAutoBuffersCount;
|
public int OutAutoBuffersCount;
|
||||||
public int InBuffersCount;
|
public int InBuffersCount;
|
||||||
public int OutBuffersCount;
|
public int OutBuffersCount;
|
||||||
public int InOutBuffersCount;
|
public int InOutBuffersCount;
|
||||||
public int InPointersCount;
|
public int InPointersCount;
|
||||||
public int OutPointersCount;
|
public int OutPointersCount;
|
||||||
public int OutFixedPointersCount;
|
public int OutFixedPointersCount;
|
||||||
public int ObjectsCount;
|
public int ObjectsCount;
|
||||||
public int HandlesCount;
|
public int HandlesCount;
|
||||||
public bool SendPid;
|
public bool SendPid;
|
||||||
#pragma warning restore CS0649
|
#pragma warning restore CS0649
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
{
|
{
|
||||||
public ReadOnlySpan<byte> Data;
|
public ReadOnlySpan<byte> Data;
|
||||||
public ReadOnlySpan<uint> Objects;
|
public ReadOnlySpan<uint> Objects;
|
||||||
public ReadOnlySpan<int> CopyHandles;
|
public ReadOnlySpan<int> CopyHandles;
|
||||||
public ReadOnlySpan<int> MoveHandles;
|
public ReadOnlySpan<int> MoveHandles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
{
|
{
|
||||||
enum CommandType
|
enum CommandType
|
||||||
{
|
{
|
||||||
Invalid = 0,
|
Invalid = 0,
|
||||||
LegacyRequest = 1,
|
LegacyRequest = 1,
|
||||||
Close = 2,
|
Close = 2,
|
||||||
LegacyControl = 3,
|
LegacyControl = 3,
|
||||||
Request = 4,
|
Request = 4,
|
||||||
Control = 5,
|
Control = 5,
|
||||||
RequestWithContext = 6,
|
RequestWithContext = 6,
|
||||||
ControlWithContext = 7
|
ControlWithContext = 7
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
|
|
||||||
var inHeader = MemoryMarshal.Cast<byte, CmifDomainInHeader>(inRawData)[0];
|
var inHeader = MemoryMarshal.Cast<byte, CmifDomainInHeader>(inRawData)[0];
|
||||||
|
|
||||||
ReadOnlySpan<byte> inDomainRawData = inRawData.Slice(Unsafe.SizeOf<CmifDomainInHeader>());
|
ReadOnlySpan<byte> inDomainRawData = inRawData[Unsafe.SizeOf<CmifDomainInHeader>()..];
|
||||||
|
|
||||||
int targetObjectId = inHeader.ObjectId;
|
int targetObjectId = inHeader.ObjectId;
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
return SfResult.InvalidHeaderSize;
|
return SfResult.InvalidHeaderSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<byte> inMessageRawData = inDomainRawData.Slice(0, inHeader.DataSize);
|
ReadOnlySpan<byte> inMessageRawData = inDomainRawData[..inHeader.DataSize];
|
||||||
|
|
||||||
if (inHeader.ObjectsCount > DomainServiceObjectProcessor.MaximumObjects)
|
if (inHeader.ObjectsCount > DomainServiceObjectProcessor.MaximumObjects)
|
||||||
{
|
{
|
||||||
|
|
|
@ -63,7 +63,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
return SfResult.InvalidInObjectsCount;
|
return SfResult.InvalidInObjectsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result result = _domain.ReserveIds(new Span<int>(_reservedObjectIds).Slice(0, OutObjectsCount));
|
Result result = _domain.ReserveIds(new Span<int>(_reservedObjectIds)[..OutObjectsCount]);
|
||||||
|
|
||||||
if (result.IsFailure)
|
if (result.IsFailure)
|
||||||
{
|
{
|
||||||
|
@ -92,7 +92,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
|
|
||||||
DebugUtil.Assert(outHeaderSize + implOutDataTotalSize + OutObjectsCount * sizeof(int) <= outRawData.Length);
|
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;
|
_outObjectIdsOffset = (response.DataWords.Length * sizeof(uint) - outRawData.Length) + implOutDataTotalSize;
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
@ -107,9 +107,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
|
|
||||||
DebugUtil.Assert(outHeaderSize + implOutDataTotalSize <= outRawData.Length);
|
DebugUtil.Assert(outHeaderSize + implOutDataTotalSize <= outRawData.Length);
|
||||||
|
|
||||||
outRawData = outRawData.Slice(outHeaderSize);
|
outRawData = outRawData[outHeaderSize..];
|
||||||
|
|
||||||
_domain.UnreserveIds(new Span<int>(_reservedObjectIds).Slice(0, OutObjectsCount));
|
_domain.UnreserveIds(new Span<int>(_reservedObjectIds)[..OutObjectsCount]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetOutObjects(scoped ref ServiceDispatchContext context, HipcMessageData response, Span<ServiceObjectHolder> outObjects)
|
public override void SetOutObjects(scoped ref ServiceDispatchContext context, HipcMessageData response, Span<ServiceObjectHolder> outObjects)
|
||||||
|
@ -129,7 +129,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
_domain.RegisterObject(objectIds[i], outObjects[i]);
|
_domain.RegisterObject(objectIds[i], outObjects[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<int> outObjectIds = MemoryMarshal.Cast<byte, int>(MemoryMarshal.Cast<uint, byte>(response.DataWords).Slice(_outObjectIdsOffset));
|
Span<int> outObjectIds = MemoryMarshal.Cast<byte, int>(MemoryMarshal.Cast<uint, byte>(response.DataWords)[_outObjectIdsOffset..]);
|
||||||
|
|
||||||
for (int i = 0; i < outObjectsCount; i++)
|
for (int i = 0; i < outObjectsCount; i++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
{
|
{
|
||||||
struct PointerAndSize
|
struct PointerAndSize
|
||||||
{
|
{
|
||||||
public static PointerAndSize Empty => new PointerAndSize(0UL, 0UL);
|
public static PointerAndSize Empty => new(0UL, 0UL);
|
||||||
|
|
||||||
public ulong Address { get; }
|
public ulong Address { get; }
|
||||||
public ulong Size { get; }
|
public ulong Size { get; }
|
||||||
|
@ -11,7 +11,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
public PointerAndSize(ulong address, ulong size)
|
public PointerAndSize(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
Address = address;
|
Address = address;
|
||||||
Size = size;
|
Size = size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,28 +2,29 @@
|
||||||
{
|
{
|
||||||
struct ServerMessageRuntimeMetadata
|
struct ServerMessageRuntimeMetadata
|
||||||
{
|
{
|
||||||
public ushort InDataSize { get; }
|
public ushort InDataSize { get; }
|
||||||
public ushort OutDataSize { get; }
|
public ushort OutDataSize { get; }
|
||||||
public byte InHeadersSize { get; }
|
public byte InHeadersSize { get; }
|
||||||
public byte OutHeadersSize { get; }
|
public byte OutHeadersSize { get; }
|
||||||
public byte InObjectsCount { get; }
|
public byte InObjectsCount { get; }
|
||||||
public byte OutObjectsCount { get; }
|
public byte OutObjectsCount { get; }
|
||||||
|
|
||||||
public int UnfixedOutPointerSizeOffset => InDataSize + InHeadersSize + 0x10;
|
public int UnfixedOutPointerSizeOffset => InDataSize + InHeadersSize + 0x10;
|
||||||
|
|
||||||
public ServerMessageRuntimeMetadata(
|
public ServerMessageRuntimeMetadata(
|
||||||
ushort inDataSize,
|
ushort inDataSize,
|
||||||
ushort outDataSize,
|
ushort outDataSize,
|
||||||
byte inHeadersSize,
|
byte inHeadersSize,
|
||||||
byte outHeadersSize,
|
byte outHeadersSize,
|
||||||
byte inObjectsCount,
|
byte inObjectsCount,
|
||||||
byte outObjectsCount)
|
byte outObjectsCount)
|
||||||
{
|
{
|
||||||
InDataSize = inDataSize;
|
InDataSize = inDataSize;
|
||||||
OutDataSize = outDataSize;
|
OutDataSize = outDataSize;
|
||||||
InHeadersSize = inHeadersSize;
|
InHeadersSize = inHeadersSize;
|
||||||
OutHeadersSize = outHeadersSize;
|
OutHeadersSize = outHeadersSize;
|
||||||
InObjectsCount = inObjectsCount;
|
InObjectsCount = inObjectsCount;
|
||||||
OutObjectsCount = outObjectsCount;
|
OutObjectsCount = outObjectsCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,14 +5,14 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
{
|
{
|
||||||
ref struct ServiceDispatchContext
|
ref struct ServiceDispatchContext
|
||||||
{
|
{
|
||||||
public IServiceObject ServiceObject;
|
public IServiceObject ServiceObject;
|
||||||
public ServerSessionManager Manager;
|
public ServerSessionManager Manager;
|
||||||
public ServerSession Session;
|
public ServerSession Session;
|
||||||
public ServerMessageProcessor Processor;
|
public ServerMessageProcessor Processor;
|
||||||
public HandlesToClose HandlesToClose;
|
public HandlesToClose HandlesToClose;
|
||||||
public PointerAndSize PointerBuffer;
|
public PointerAndSize PointerBuffer;
|
||||||
public ReadOnlySpan<byte> InMessageBuffer;
|
public ReadOnlySpan<byte> InMessageBuffer;
|
||||||
public Span<byte> OutMessageBuffer;
|
public Span<byte> OutMessageBuffer;
|
||||||
public HipcMessage Request;
|
public HipcMessage Request;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
public ServiceDispatchTable(string objectName, IReadOnlyDictionary<int, CommandHandler> entries)
|
public ServiceDispatchTable(string objectName, IReadOnlyDictionary<int, CommandHandler> entries)
|
||||||
{
|
{
|
||||||
_objectName = objectName;
|
_objectName = objectName;
|
||||||
_entries = entries;
|
_entries = entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData)
|
public override Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData)
|
||||||
|
@ -30,4 +30,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
return new ServiceDispatchTable(instance.GetType().Name, instance.GetCommandHandlers());
|
return new ServiceDispatchTable(instance.GetType().Name, instance.GetCommandHandlers());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -39,17 +39,21 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
|
|
||||||
if (!entries.TryGetValue((int)commandId, out var commandHandler))
|
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 (HorizonStatic.Options.IgnoreMissingServices)
|
||||||
{
|
{
|
||||||
// If ignore missing services is enabled, just pretend that everything is fine.
|
// If ignore missing services is enabled, just pretend that everything is fine.
|
||||||
var response = PrepareForStubReply(ref context, out Span<byte> outRawData);
|
PrepareForStubReply(ref context, out Span<byte> outRawData);
|
||||||
CommandHandler.GetCmifOutHeaderPointer(ref outHeader, ref outRawData);
|
CommandHandler.GetCmifOutHeaderPointer(ref outHeader, ref outRawData);
|
||||||
outHeader[0] = new CmifOutHeader() { Magic = CmifMessage.CmifOutHeaderMagic, Result = Result.Success };
|
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;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
else if (HorizonStatic.Options.ThrowOnInvalidCommandIds)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException($"{objectName} command ID: {commandId} is not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
return SfResult.UnknownCommandId;
|
return SfResult.UnknownCommandId;
|
||||||
}
|
}
|
||||||
|
@ -72,6 +76,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
if (outHeader.IsEmpty)
|
if (outHeader.IsEmpty)
|
||||||
{
|
{
|
||||||
commandResult.AbortOnSuccess();
|
commandResult.AbortOnSuccess();
|
||||||
|
|
||||||
return commandResult;
|
return commandResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,11 +85,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HipcMessageData PrepareForStubReply(scoped ref ServiceDispatchContext context, out Span<byte> outRawData)
|
private static void PrepareForStubReply(scoped ref ServiceDispatchContext context, out Span<byte> outRawData)
|
||||||
{
|
{
|
||||||
var response = HipcMessage.WriteResponse(context.OutMessageBuffer, 0, 0x20 / sizeof(uint), 0, 0);
|
var response = HipcMessage.WriteResponse(context.OutMessageBuffer, 0, 0x20 / sizeof(uint), 0, 0);
|
||||||
outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords);
|
outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords);
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -20,37 +20,37 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
|
|
||||||
struct CommandArg
|
struct CommandArg
|
||||||
{
|
{
|
||||||
public CommandArgType Type { get; }
|
public CommandArgType Type { get; }
|
||||||
public HipcBufferFlags BufferFlags { get; }
|
public HipcBufferFlags BufferFlags { get; }
|
||||||
public ushort BufferFixedSize { get; }
|
public ushort BufferFixedSize { get; }
|
||||||
public int ArgSize { get; }
|
public int ArgSize { get; }
|
||||||
public int ArgAlignment { get; }
|
public int ArgAlignment { get; }
|
||||||
|
|
||||||
public CommandArg(CommandArgType type)
|
public CommandArg(CommandArgType type)
|
||||||
{
|
{
|
||||||
Type = type;
|
Type = type;
|
||||||
BufferFlags = default;
|
BufferFlags = default;
|
||||||
BufferFixedSize = 0;
|
BufferFixedSize = 0;
|
||||||
ArgSize = 0;
|
ArgSize = 0;
|
||||||
ArgAlignment = 0;
|
ArgAlignment = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommandArg(CommandArgType type, int argSize, int argAlignment)
|
public CommandArg(CommandArgType type, int argSize, int argAlignment)
|
||||||
{
|
{
|
||||||
Type = type;
|
Type = type;
|
||||||
BufferFlags = default;
|
BufferFlags = default;
|
||||||
BufferFixedSize = 0;
|
BufferFixedSize = 0;
|
||||||
ArgSize = argSize;
|
ArgSize = argSize;
|
||||||
ArgAlignment = argAlignment;
|
ArgAlignment = argAlignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommandArg(HipcBufferFlags flags, ushort fixedSize = 0)
|
public CommandArg(HipcBufferFlags flags, ushort fixedSize = 0)
|
||||||
{
|
{
|
||||||
Type = CommandArgType.Buffer;
|
Type = CommandArgType.Buffer;
|
||||||
BufferFlags = flags;
|
BufferFlags = flags;
|
||||||
BufferFixedSize = fixedSize;
|
BufferFixedSize = fixedSize;
|
||||||
ArgSize = 0;
|
ArgSize = 0;
|
||||||
ArgAlignment = 0;
|
ArgAlignment = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,8 +6,8 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
[AttributeUsage(AttributeTargets.Parameter)]
|
[AttributeUsage(AttributeTargets.Parameter)]
|
||||||
class BufferAttribute : Attribute
|
class BufferAttribute : Attribute
|
||||||
{
|
{
|
||||||
public HipcBufferFlags Flags { get; }
|
public HipcBufferFlags Flags { get; }
|
||||||
public ushort FixedSize { get; }
|
public ushort FixedSize { get; }
|
||||||
|
|
||||||
public BufferAttribute(HipcBufferFlags flags)
|
public BufferAttribute(HipcBufferFlags flags)
|
||||||
{
|
{
|
||||||
|
@ -16,7 +16,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
|
|
||||||
public BufferAttribute(HipcBufferFlags flags, ushort fixedSize)
|
public BufferAttribute(HipcBufferFlags flags, ushort fixedSize)
|
||||||
{
|
{
|
||||||
Flags = flags | HipcBufferFlags.FixedSize;
|
Flags = flags | HipcBufferFlags.FixedSize;
|
||||||
FixedSize = fixedSize;
|
FixedSize = fixedSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,4 +35,4 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
class MoveHandleAttribute : Attribute
|
class MoveHandleAttribute : Attribute
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,20 +9,20 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
class CommandHandler
|
class CommandHandler
|
||||||
{
|
{
|
||||||
public delegate Result MethodInvoke(
|
public delegate Result MethodInvoke(
|
||||||
ref ServiceDispatchContext context,
|
ref ServiceDispatchContext context,
|
||||||
HipcCommandProcessor processor,
|
HipcCommandProcessor processor,
|
||||||
ServerMessageRuntimeMetadata runtimeMetadata,
|
ServerMessageRuntimeMetadata runtimeMetadata,
|
||||||
ReadOnlySpan<byte> inRawData,
|
ReadOnlySpan<byte> inRawData,
|
||||||
ref Span<CmifOutHeader> outHeader);
|
ref Span<CmifOutHeader> outHeader);
|
||||||
|
|
||||||
private readonly MethodInvoke _invoke;
|
private readonly MethodInvoke _invoke;
|
||||||
private readonly HipcCommandProcessor _processor;
|
private readonly HipcCommandProcessor _processor;
|
||||||
|
|
||||||
public string MethodName => _invoke.Method.Name;
|
public string MethodName => _invoke.Method.Name;
|
||||||
|
|
||||||
public CommandHandler(MethodInvoke invoke, params CommandArg[] args)
|
public CommandHandler(MethodInvoke invoke, params CommandArg[] args)
|
||||||
{
|
{
|
||||||
_invoke = invoke;
|
_invoke = invoke;
|
||||||
_processor = new HipcCommandProcessor(args);
|
_processor = new HipcCommandProcessor(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,8 +37,8 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
context.Processor.SetImplementationProcessor(_processor);
|
context.Processor.SetImplementationProcessor(_processor);
|
||||||
}
|
}
|
||||||
|
|
||||||
var runtimeMetadata = context.Processor.GetRuntimeMetadata();
|
var runtimeMetadata = context.Processor.GetRuntimeMetadata();
|
||||||
Result result = context.Processor.PrepareForProcess(ref context, runtimeMetadata);
|
Result result = context.Processor.PrepareForProcess(ref context, runtimeMetadata);
|
||||||
|
|
||||||
if (result.IsFailure)
|
if (result.IsFailure)
|
||||||
{
|
{
|
||||||
|
@ -50,8 +50,8 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
|
|
||||||
public static void GetCmifOutHeaderPointer(ref Span<CmifOutHeader> outHeader, ref Span<byte> outRawData)
|
public static void GetCmifOutHeaderPointer(ref Span<CmifOutHeader> outHeader, ref Span<byte> outRawData)
|
||||||
{
|
{
|
||||||
outHeader = MemoryMarshal.Cast<byte, CmifOutHeader>(outRawData).Slice(0, 1);
|
outHeader = MemoryMarshal.Cast<byte, CmifOutHeader>(outRawData)[..1];
|
||||||
outRawData = outRawData.Slice(Unsafe.SizeOf<CmifOutHeader>());
|
outRawData = outRawData[Unsafe.SizeOf<CmifOutHeader>()..];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -22,6 +22,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
public static ref T GetRef<T>(PointerAndSize bufferRange) where T : unmanaged
|
public static ref T GetRef<T>(PointerAndSize bufferRange) where T : unmanaged
|
||||||
{
|
{
|
||||||
var writableRegion = GetWritableRegion(bufferRange);
|
var writableRegion = GetWritableRegion(bufferRange);
|
||||||
|
|
||||||
return ref MemoryMarshal.Cast<byte, T>(writableRegion.Memory.Span)[0];
|
return ref MemoryMarshal.Cast<byte, T>(writableRegion.Memory.Span)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,4 +66,4 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
response.MoveHandles[index] = value;
|
response.MoveHandles[index] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,12 +5,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
[Flags]
|
[Flags]
|
||||||
enum HipcBufferFlags : byte
|
enum HipcBufferFlags : byte
|
||||||
{
|
{
|
||||||
In = 1 << 0,
|
In = 1 << 0,
|
||||||
Out = 1 << 1,
|
Out = 1 << 1,
|
||||||
MapAlias = 1 << 2,
|
MapAlias = 1 << 2,
|
||||||
Pointer = 1 << 3,
|
Pointer = 1 << 3,
|
||||||
FixedSize = 1 << 4,
|
FixedSize = 1 << 4,
|
||||||
AutoSelect = 1 << 5,
|
AutoSelect = 1 << 5,
|
||||||
MapTransferAllowsNonSecure = 1 << 6,
|
MapTransferAllowsNonSecure = 1 << 6,
|
||||||
MapTransferAllowsNonDevice = 1 << 7
|
MapTransferAllowsNonDevice = 1 << 7
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
{
|
{
|
||||||
enum HipcBufferMode
|
enum HipcBufferMode
|
||||||
{
|
{
|
||||||
Normal = 0,
|
Normal = 0,
|
||||||
NonSecure = 1,
|
NonSecure = 1,
|
||||||
Invalid = 2,
|
Invalid = 2,
|
||||||
NonDevice = 3
|
NonDevice = 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
{
|
{
|
||||||
clientHandle = 0;
|
clientHandle = 0;
|
||||||
|
|
||||||
if (!(_session.ServiceObjectHolder.ServiceObject is DomainServiceObject domain))
|
if (_session.ServiceObjectHolder.ServiceObject is not DomainServiceObject domain)
|
||||||
{
|
{
|
||||||
return HipcResult.TargetNotDomain;
|
return HipcResult.TargetNotDomain;
|
||||||
}
|
}
|
||||||
|
@ -112,4 +112,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,9 +10,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
{
|
{
|
||||||
public const int AutoReceiveStatic = byte.MaxValue;
|
public const int AutoReceiveStatic = byte.MaxValue;
|
||||||
|
|
||||||
public HipcMetadata Meta;
|
public HipcMetadata Meta;
|
||||||
public HipcMessageData Data;
|
public HipcMessageData Data;
|
||||||
public ulong Pid;
|
public ulong Pid;
|
||||||
|
|
||||||
public HipcMessage(Span<byte> data)
|
public HipcMessage(Span<byte> data)
|
||||||
{
|
{
|
||||||
|
@ -20,10 +20,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
|
|
||||||
Header header = MemoryMarshal.Cast<byte, Header>(data)[0];
|
Header header = MemoryMarshal.Cast<byte, Header>(data)[0];
|
||||||
|
|
||||||
data = data.Slice(Unsafe.SizeOf<Header>());
|
data = data[Unsafe.SizeOf<Header>()..];
|
||||||
|
|
||||||
int receiveStaticsCount = 0;
|
int receiveStaticsCount = 0;
|
||||||
ulong pid = 0;
|
ulong pid = 0;
|
||||||
|
|
||||||
if (header.ReceiveStaticMode != 0)
|
if (header.ReceiveStaticMode != 0)
|
||||||
{
|
{
|
||||||
|
@ -42,46 +42,44 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
if (header.HasSpecialHeader)
|
if (header.HasSpecialHeader)
|
||||||
{
|
{
|
||||||
specialHeader = MemoryMarshal.Cast<byte, SpecialHeader>(data)[0];
|
specialHeader = MemoryMarshal.Cast<byte, SpecialHeader>(data)[0];
|
||||||
|
data = data[Unsafe.SizeOf<SpecialHeader>()..];
|
||||||
data = data.Slice(Unsafe.SizeOf<SpecialHeader>());
|
|
||||||
|
|
||||||
if (specialHeader.SendPid)
|
if (specialHeader.SendPid)
|
||||||
{
|
{
|
||||||
pid = MemoryMarshal.Cast<byte, ulong>(data)[0];
|
pid = MemoryMarshal.Cast<byte, ulong>(data)[0];
|
||||||
|
data = data[sizeof(ulong)..];
|
||||||
data = data.Slice(sizeof(ulong));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Meta = new HipcMetadata()
|
Meta = new HipcMetadata()
|
||||||
{
|
{
|
||||||
Type = (int)header.Type,
|
Type = (int)header.Type,
|
||||||
SendStaticsCount = header.SendStaticsCount,
|
SendStaticsCount = header.SendStaticsCount,
|
||||||
SendBuffersCount = header.SendBuffersCount,
|
SendBuffersCount = header.SendBuffersCount,
|
||||||
ReceiveBuffersCount = header.ReceiveBuffersCount,
|
ReceiveBuffersCount = header.ReceiveBuffersCount,
|
||||||
ExchangeBuffersCount = header.ExchangeBuffersCount,
|
ExchangeBuffersCount = header.ExchangeBuffersCount,
|
||||||
DataWordsCount = header.DataWordsCount,
|
DataWordsCount = header.DataWordsCount,
|
||||||
ReceiveStaticsCount = receiveStaticsCount,
|
ReceiveStaticsCount = receiveStaticsCount,
|
||||||
SendPid = specialHeader.SendPid,
|
SendPid = specialHeader.SendPid,
|
||||||
CopyHandlesCount = specialHeader.CopyHandlesCount,
|
CopyHandlesCount = specialHeader.CopyHandlesCount,
|
||||||
MoveHandlesCount = specialHeader.MoveHandlesCount
|
MoveHandlesCount = specialHeader.MoveHandlesCount
|
||||||
};
|
};
|
||||||
|
|
||||||
Data = CreateMessageData(Meta, data, initialLength);
|
Data = CreateMessageData(Meta, data, initialLength);
|
||||||
Pid = pid;
|
Pid = pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HipcMessageData WriteResponse(
|
public static HipcMessageData WriteResponse(
|
||||||
Span<byte> destination,
|
Span<byte> destination,
|
||||||
int sendStaticCount,
|
int sendStaticCount,
|
||||||
int dataWordsCount,
|
int dataWordsCount,
|
||||||
int copyHandlesCount,
|
int copyHandlesCount,
|
||||||
int moveHandlesCount)
|
int moveHandlesCount)
|
||||||
{
|
{
|
||||||
return WriteMessage(destination, new HipcMetadata()
|
return WriteMessage(destination, new HipcMetadata()
|
||||||
{
|
{
|
||||||
SendStaticsCount = sendStaticCount,
|
SendStaticsCount = sendStaticCount,
|
||||||
DataWordsCount = dataWordsCount,
|
DataWordsCount = dataWordsCount,
|
||||||
CopyHandlesCount = copyHandlesCount,
|
CopyHandlesCount = copyHandlesCount,
|
||||||
MoveHandlesCount = moveHandlesCount
|
MoveHandlesCount = moveHandlesCount
|
||||||
});
|
});
|
||||||
|
@ -89,38 +87,37 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
|
|
||||||
public static HipcMessageData WriteMessage(Span<byte> destination, HipcMetadata meta)
|
public static HipcMessageData WriteMessage(Span<byte> destination, HipcMetadata meta)
|
||||||
{
|
{
|
||||||
int initialLength = destination.Length;
|
int initialLength = destination.Length;
|
||||||
|
|
||||||
bool hasSpecialHeader = meta.SendPid || meta.CopyHandlesCount != 0 || meta.MoveHandlesCount != 0;
|
bool hasSpecialHeader = meta.SendPid || meta.CopyHandlesCount != 0 || meta.MoveHandlesCount != 0;
|
||||||
|
|
||||||
MemoryMarshal.Cast<byte, Header>(destination)[0] = new Header()
|
MemoryMarshal.Cast<byte, Header>(destination)[0] = new Header()
|
||||||
{
|
{
|
||||||
Type = (CommandType)meta.Type,
|
Type = (CommandType)meta.Type,
|
||||||
SendStaticsCount = meta.SendStaticsCount,
|
SendStaticsCount = meta.SendStaticsCount,
|
||||||
SendBuffersCount = meta.SendBuffersCount,
|
SendBuffersCount = meta.SendBuffersCount,
|
||||||
ReceiveBuffersCount = meta.ReceiveBuffersCount,
|
ReceiveBuffersCount = meta.ReceiveBuffersCount,
|
||||||
ExchangeBuffersCount = meta.ExchangeBuffersCount,
|
ExchangeBuffersCount = meta.ExchangeBuffersCount,
|
||||||
DataWordsCount = meta.DataWordsCount,
|
DataWordsCount = meta.DataWordsCount,
|
||||||
ReceiveStaticMode = meta.ReceiveStaticsCount != 0 ? (meta.ReceiveStaticsCount != AutoReceiveStatic ? meta.ReceiveStaticsCount + 2 : 2) : 0,
|
ReceiveStaticMode = meta.ReceiveStaticsCount != 0 ? (meta.ReceiveStaticsCount != AutoReceiveStatic ? meta.ReceiveStaticsCount + 2 : 2) : 0,
|
||||||
HasSpecialHeader = hasSpecialHeader
|
HasSpecialHeader = hasSpecialHeader
|
||||||
};
|
};
|
||||||
|
|
||||||
destination = destination.Slice(Unsafe.SizeOf<Header>());
|
destination = destination[Unsafe.SizeOf<Header>()..];
|
||||||
|
|
||||||
if (hasSpecialHeader)
|
if (hasSpecialHeader)
|
||||||
{
|
{
|
||||||
MemoryMarshal.Cast<byte, SpecialHeader>(destination)[0] = new SpecialHeader()
|
MemoryMarshal.Cast<byte, SpecialHeader>(destination)[0] = new SpecialHeader()
|
||||||
{
|
{
|
||||||
SendPid = meta.SendPid,
|
SendPid = meta.SendPid,
|
||||||
CopyHandlesCount = meta.CopyHandlesCount,
|
CopyHandlesCount = meta.CopyHandlesCount,
|
||||||
MoveHandlesCount = meta.MoveHandlesCount
|
MoveHandlesCount = meta.MoveHandlesCount
|
||||||
};
|
};
|
||||||
|
|
||||||
destination = destination.Slice(Unsafe.SizeOf<SpecialHeader>());
|
destination = destination[Unsafe.SizeOf<SpecialHeader>()..];
|
||||||
|
|
||||||
if (meta.SendPid)
|
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)
|
if (meta.CopyHandlesCount != 0)
|
||||||
{
|
{
|
||||||
copyHandles = MemoryMarshal.Cast<byte, int>(data).Slice(0, meta.CopyHandlesCount);
|
copyHandles = MemoryMarshal.Cast<byte, int>(data)[..meta.CopyHandlesCount];
|
||||||
|
|
||||||
data = data.Slice(meta.CopyHandlesCount * sizeof(int));
|
data = data[(meta.CopyHandlesCount * sizeof(int))..];
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<int> moveHandles = Span<int>.Empty;
|
Span<int> moveHandles = Span<int>.Empty;
|
||||||
|
|
||||||
if (meta.MoveHandlesCount != 0)
|
if (meta.MoveHandlesCount != 0)
|
||||||
{
|
{
|
||||||
moveHandles = MemoryMarshal.Cast<byte, int>(data).Slice(0, meta.MoveHandlesCount);
|
moveHandles = MemoryMarshal.Cast<byte, int>(data)[..meta.MoveHandlesCount];
|
||||||
|
|
||||||
data = data.Slice(meta.MoveHandlesCount * sizeof(int));
|
data = data[(meta.MoveHandlesCount * sizeof(int))..];
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<HipcStaticDescriptor> sendStatics = Span<HipcStaticDescriptor>.Empty;
|
Span<HipcStaticDescriptor> sendStatics = Span<HipcStaticDescriptor>.Empty;
|
||||||
|
|
||||||
if (meta.SendStaticsCount != 0)
|
if (meta.SendStaticsCount != 0)
|
||||||
{
|
{
|
||||||
sendStatics = MemoryMarshal.Cast<byte, HipcStaticDescriptor>(data).Slice(0, meta.SendStaticsCount);
|
sendStatics = MemoryMarshal.Cast<byte, HipcStaticDescriptor>(data)[..meta.SendStaticsCount];
|
||||||
|
|
||||||
data = data.Slice(meta.SendStaticsCount * Unsafe.SizeOf<HipcStaticDescriptor>());
|
data = data[(meta.SendStaticsCount * Unsafe.SizeOf<HipcStaticDescriptor>())..];
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<HipcBufferDescriptor> sendBuffers = Span<HipcBufferDescriptor>.Empty;
|
Span<HipcBufferDescriptor> sendBuffers = Span<HipcBufferDescriptor>.Empty;
|
||||||
|
|
||||||
if (meta.SendBuffersCount != 0)
|
if (meta.SendBuffersCount != 0)
|
||||||
{
|
{
|
||||||
sendBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data).Slice(0, meta.SendBuffersCount);
|
sendBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data)[..meta.SendBuffersCount];
|
||||||
|
|
||||||
data = data.Slice(meta.SendBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>());
|
data = data[(meta.SendBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>())..];
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<HipcBufferDescriptor> receiveBuffers = Span<HipcBufferDescriptor>.Empty;
|
Span<HipcBufferDescriptor> receiveBuffers = Span<HipcBufferDescriptor>.Empty;
|
||||||
|
|
||||||
if (meta.ReceiveBuffersCount != 0)
|
if (meta.ReceiveBuffersCount != 0)
|
||||||
{
|
{
|
||||||
receiveBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data).Slice(0, meta.ReceiveBuffersCount);
|
receiveBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data)[..meta.ReceiveBuffersCount];
|
||||||
|
|
||||||
data = data.Slice(meta.ReceiveBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>());
|
data = data[(meta.ReceiveBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>())..];
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<HipcBufferDescriptor> exchangeBuffers = Span<HipcBufferDescriptor>.Empty;
|
Span<HipcBufferDescriptor> exchangeBuffers = Span<HipcBufferDescriptor>.Empty;
|
||||||
|
|
||||||
if (meta.ExchangeBuffersCount != 0)
|
if (meta.ExchangeBuffersCount != 0)
|
||||||
{
|
{
|
||||||
exchangeBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data).Slice(0, meta.ExchangeBuffersCount);
|
exchangeBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data)[..meta.ExchangeBuffersCount];
|
||||||
|
|
||||||
data = data.Slice(meta.ExchangeBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>());
|
data = data[(meta.ExchangeBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>())..];
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<uint> dataWords = Span<uint>.Empty;
|
Span<uint> dataWords = Span<uint>.Empty;
|
||||||
|
|
||||||
if (meta.DataWordsCount != 0)
|
if (meta.DataWordsCount != 0)
|
||||||
{
|
{
|
||||||
int dataOffset = initialLength - data.Length;
|
int dataOffset = initialLength - data.Length;
|
||||||
int dataOffsetAligned = BitUtils.AlignUp(dataOffset, 0x10);
|
int dataOffsetAligned = BitUtils.AlignUp(dataOffset, 0x10);
|
||||||
|
int padding = (dataOffsetAligned - dataOffset) / sizeof(uint);
|
||||||
|
|
||||||
int padding = (dataOffsetAligned - dataOffset) / sizeof(uint);
|
dataWords = MemoryMarshal.Cast<byte, uint>(data)[padding..meta.DataWordsCount];
|
||||||
|
|
||||||
dataWords = MemoryMarshal.Cast<byte, uint>(data).Slice(padding, meta.DataWordsCount - padding);
|
data = data[(meta.DataWordsCount * sizeof(uint))..];
|
||||||
|
|
||||||
data = data.Slice(meta.DataWordsCount * sizeof(uint));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<HipcReceiveListEntry> receiveList = Span<HipcReceiveListEntry>.Empty;
|
Span<HipcReceiveListEntry> receiveList = Span<HipcReceiveListEntry>.Empty;
|
||||||
|
@ -203,19 +199,19 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
{
|
{
|
||||||
int receiveListSize = meta.ReceiveStaticsCount == AutoReceiveStatic ? 1 : meta.ReceiveStaticsCount;
|
int receiveListSize = meta.ReceiveStaticsCount == AutoReceiveStatic ? 1 : meta.ReceiveStaticsCount;
|
||||||
|
|
||||||
receiveList = MemoryMarshal.Cast<byte, HipcReceiveListEntry>(data).Slice(0, receiveListSize);
|
receiveList = MemoryMarshal.Cast<byte, HipcReceiveListEntry>(data)[..receiveListSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HipcMessageData()
|
return new HipcMessageData()
|
||||||
{
|
{
|
||||||
SendStatics = sendStatics,
|
SendStatics = sendStatics,
|
||||||
SendBuffers = sendBuffers,
|
SendBuffers = sendBuffers,
|
||||||
ReceiveBuffers = receiveBuffers,
|
ReceiveBuffers = receiveBuffers,
|
||||||
ExchangeBuffers = exchangeBuffers,
|
ExchangeBuffers = exchangeBuffers,
|
||||||
DataWords = dataWords,
|
DataWords = dataWords,
|
||||||
ReceiveList = receiveList,
|
ReceiveList = receiveList,
|
||||||
CopyHandles = copyHandles,
|
CopyHandles = copyHandles,
|
||||||
MoveHandles = moveHandles
|
MoveHandles = moveHandles
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
public Span<HipcBufferDescriptor> SendBuffers;
|
public Span<HipcBufferDescriptor> SendBuffers;
|
||||||
public Span<HipcBufferDescriptor> ReceiveBuffers;
|
public Span<HipcBufferDescriptor> ReceiveBuffers;
|
||||||
public Span<HipcBufferDescriptor> ExchangeBuffers;
|
public Span<HipcBufferDescriptor> ExchangeBuffers;
|
||||||
public Span<uint> DataWords;
|
public Span<uint> DataWords;
|
||||||
public Span<HipcReceiveListEntry> ReceiveList;
|
public Span<HipcReceiveListEntry> ReceiveList;
|
||||||
public Span<int> CopyHandles;
|
public Span<int> CopyHandles;
|
||||||
public Span<int> MoveHandles;
|
public Span<int> MoveHandles;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,15 +2,15 @@
|
||||||
{
|
{
|
||||||
struct HipcMetadata
|
struct HipcMetadata
|
||||||
{
|
{
|
||||||
public int Type;
|
public int Type;
|
||||||
public int SendStaticsCount;
|
public int SendStaticsCount;
|
||||||
public int SendBuffersCount;
|
public int SendBuffersCount;
|
||||||
public int ReceiveBuffersCount;
|
public int ReceiveBuffersCount;
|
||||||
public int ExchangeBuffersCount;
|
public int ExchangeBuffersCount;
|
||||||
public int DataWordsCount;
|
public int DataWordsCount;
|
||||||
public int ReceiveStaticsCount;
|
public int ReceiveStaticsCount;
|
||||||
public bool SendPid;
|
public bool SendPid;
|
||||||
public int CopyHandlesCount;
|
public int CopyHandlesCount;
|
||||||
public int MoveHandlesCount;
|
public int MoveHandlesCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
public HipcReceiveListEntry(ulong address, ulong size)
|
public HipcReceiveListEntry(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
_addressLow = (uint)address;
|
_addressLow = (uint)address;
|
||||||
_word1 = (ushort)(address >> 32) | (uint)(size << 16);
|
_word1 = (ushort)(address >> 32) | (uint)(size << 16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,17 +6,14 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
{
|
{
|
||||||
public const int ModuleId = 11;
|
public const int ModuleId = 11;
|
||||||
|
|
||||||
public static Result OutOfSessionMemory => new Result(ModuleId, 102);
|
public static Result OutOfSessionMemory => new(ModuleId, 102);
|
||||||
public static Result OutOfSessions => new Result(ModuleId, 131);
|
public static Result OutOfSessions => new(ModuleId, 131);
|
||||||
public static Result PointerBufferTooSmall => new Result(ModuleId, 141);
|
public static Result PointerBufferTooSmall => new(ModuleId, 141);
|
||||||
public static Result OutOfDomains => new Result(ModuleId, 200);
|
public static Result OutOfDomains => new(ModuleId, 200);
|
||||||
|
public static Result InvalidRequestSize => new(ModuleId, 402);
|
||||||
public static Result InvalidRequestSize => new Result(ModuleId, 402);
|
public static Result UnknownCommandType => new(ModuleId, 403);
|
||||||
public static Result UnknownCommandType => new Result(ModuleId, 403);
|
public static Result InvalidCmifRequest => new(ModuleId, 420);
|
||||||
|
public static Result TargetNotDomain => new(ModuleId, 491);
|
||||||
public static Result InvalidCmifRequest => new Result(ModuleId, 420);
|
public static Result DomainObjectNotFound => new(ModuleId, 492);
|
||||||
|
|
||||||
public static Result TargetNotDomain => new Result(ModuleId, 491);
|
|
||||||
public static Result DomainObjectNotFound => new Result(ModuleId, 492);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
{
|
{
|
||||||
private readonly ulong _data;
|
private readonly ulong _data;
|
||||||
|
|
||||||
public ulong Address => ((((_data >> 2) & 0x70) | ((_data >> 12) & 0xf)) << 32) | (_data >> 32);
|
public ulong Address => ((((_data >> 2) & 0x70) | ((_data >> 12) & 0xf)) << 32) | (_data >> 32);
|
||||||
public ushort Size => (ushort)(_data >> 16);
|
public ushort Size => (ushort)(_data >> 16);
|
||||||
public int ReceiveIndex => (int)(_data & 0xf);
|
public int ReceiveIndex => (int)(_data & 0xf);
|
||||||
|
|
||||||
public HipcStaticDescriptor(ulong address, ushort size, int receiveIndex)
|
public HipcStaticDescriptor(ulong address, ushort size, int receiveIndex)
|
||||||
{
|
{
|
||||||
|
@ -19,4 +19,4 @@
|
||||||
_data = data;
|
_data = data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,19 +2,19 @@
|
||||||
{
|
{
|
||||||
struct ManagerOptions
|
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 PointerBufferSize { get; }
|
||||||
public int MaxDomains { get; }
|
public int MaxDomains { get; }
|
||||||
public int MaxDomainObjects { get; }
|
public int MaxDomainObjects { get; }
|
||||||
public bool CanDeferInvokeRequest { get; }
|
public bool CanDeferInvokeRequest { get; }
|
||||||
|
|
||||||
public ManagerOptions(int pointerBufferSize, int maxDomains, int maxDomainObjects, bool canDeferInvokeRequest)
|
public ManagerOptions(int pointerBufferSize, int maxDomains, int maxDomainObjects, bool canDeferInvokeRequest)
|
||||||
{
|
{
|
||||||
PointerBufferSize = pointerBufferSize;
|
PointerBufferSize = pointerBufferSize;
|
||||||
MaxDomains = maxDomains;
|
MaxDomains = maxDomains;
|
||||||
MaxDomainObjects = maxDomainObjects;
|
MaxDomainObjects = maxDomainObjects;
|
||||||
CanDeferInvokeRequest = canDeferInvokeRequest;
|
CanDeferInvokeRequest = canDeferInvokeRequest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,22 +6,22 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
{
|
{
|
||||||
class Server : MultiWaitHolderOfHandle
|
class Server : MultiWaitHolderOfHandle
|
||||||
{
|
{
|
||||||
public int PortIndex { get; }
|
public int PortIndex { get; }
|
||||||
public int PortHandle { get; }
|
public int PortHandle { get; }
|
||||||
public ServiceName Name { get; }
|
public ServiceName Name { get; }
|
||||||
public bool Managed { get; }
|
public bool Managed { get; }
|
||||||
public ServiceObjectHolder StaticObject { get; }
|
public ServiceObjectHolder StaticObject { get; }
|
||||||
|
|
||||||
public Server(
|
public Server(
|
||||||
int portIndex,
|
int portIndex,
|
||||||
int portHandle,
|
int portHandle,
|
||||||
ServiceName name,
|
ServiceName name,
|
||||||
bool managed,
|
bool managed,
|
||||||
ServiceObjectHolder staticHoder) : base(portHandle)
|
ServiceObjectHolder staticHoder) : base(portHandle)
|
||||||
{
|
{
|
||||||
PortHandle = portHandle;
|
PortHandle = portHandle;
|
||||||
Name = name;
|
Name = name;
|
||||||
Managed = managed;
|
Managed = managed;
|
||||||
|
|
||||||
if (staticHoder != null)
|
if (staticHoder != null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
|
|
||||||
protected override Result DispatchManagerRequest(ServerSession session, Span<byte> inMessage, Span<byte> outMessage)
|
protected override Result DispatchManagerRequest(ServerSession session, Span<byte> inMessage, Span<byte> outMessage)
|
||||||
{
|
{
|
||||||
HipcManager hipcManager = new HipcManager(this, session);
|
HipcManager hipcManager = new(this, session);
|
||||||
|
|
||||||
return DispatchRequest(new ServiceObjectHolder(hipcManager), session, inMessage, outMessage);
|
return DispatchRequest(new ServiceObjectHolder(hipcManager), session, inMessage, outMessage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerSession session = new ServerSession(sessionIndex, sessionHandle, obj);
|
ServerSession session = new(sessionIndex, sessionHandle, obj);
|
||||||
|
|
||||||
_sessions.Add(session);
|
_sessions.Add(session);
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
{
|
{
|
||||||
lock (_resourceLock)
|
lock (_resourceLock)
|
||||||
{
|
{
|
||||||
Server server = new Server(portIndex, portHandle, name, managed, staticHoder);
|
Server server = new(portIndex, portHandle, name, managed, staticHoder);
|
||||||
|
|
||||||
_servers.Add(server);
|
_servers.Add(server);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
|
|
||||||
private enum UserDataTag
|
private enum UserDataTag
|
||||||
{
|
{
|
||||||
Server = 1,
|
Server = 1,
|
||||||
Session = 2
|
Session = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,16 +36,17 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
_canDeferInvokeRequest = options.CanDeferInvokeRequest;
|
_canDeferInvokeRequest = options.CanDeferInvokeRequest;
|
||||||
|
|
||||||
_multiWait = new MultiWait();
|
_multiWait = new MultiWait();
|
||||||
_waitList = new MultiWait();
|
_waitList = new MultiWait();
|
||||||
|
|
||||||
_multiWaitSelectionLock = new object();
|
_multiWaitSelectionLock = new object();
|
||||||
_waitListLock = new object();
|
_waitListLock = new object();
|
||||||
|
|
||||||
_requestStopEvent = new Event(EventClearMode.ManualClear);
|
_requestStopEvent = new Event(EventClearMode.ManualClear);
|
||||||
_notifyEvent = new Event(EventClearMode.ManualClear);
|
_notifyEvent = new Event(EventClearMode.ManualClear);
|
||||||
|
|
||||||
_requestStopEventHolder = new MultiWaitHolderOfEvent(_requestStopEvent);
|
_requestStopEventHolder = new MultiWaitHolderOfEvent(_requestStopEvent);
|
||||||
_multiWait.LinkMultiWaitHolder(_requestStopEventHolder);
|
_multiWait.LinkMultiWaitHolder(_requestStopEventHolder);
|
||||||
|
|
||||||
_notifyEventHolder = new MultiWaitHolderOfEvent(_notifyEvent);
|
_notifyEventHolder = new MultiWaitHolderOfEvent(_notifyEvent);
|
||||||
_multiWait.LinkMultiWaitHolder(_notifyEventHolder);
|
_multiWait.LinkMultiWaitHolder(_notifyEventHolder);
|
||||||
}
|
}
|
||||||
|
@ -73,6 +74,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
private void RegisterServerImpl(int portIndex, ServiceObjectHolder staticHolder, int portHandle)
|
private void RegisterServerImpl(int portIndex, ServiceObjectHolder staticHolder, int portHandle)
|
||||||
{
|
{
|
||||||
Server server = AllocateServer(portIndex, portHandle, ServiceName.Invalid, managed: false, staticHolder);
|
Server server = AllocateServer(portIndex, portHandle, ServiceName.Invalid, managed: false, staticHolder);
|
||||||
|
|
||||||
RegisterServerImpl(server);
|
RegisterServerImpl(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +88,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
}
|
}
|
||||||
|
|
||||||
Server server = AllocateServer(portIndex, portHandle, name, managed: true, staticHolder);
|
Server server = AllocateServer(portIndex, portHandle, name, managed: true, staticHolder);
|
||||||
|
|
||||||
RegisterServerImpl(server);
|
RegisterServerImpl(server);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
@ -103,6 +106,11 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Result AcceptImpl(Server server, IServiceObject obj)
|
||||||
|
{
|
||||||
|
return AcceptSession(server.PortHandle, new ServiceObjectHolder(obj));
|
||||||
|
}
|
||||||
|
|
||||||
public void ServiceRequests()
|
public void ServiceRequests()
|
||||||
{
|
{
|
||||||
while (WaitAndProcessRequestsImpl());
|
while (WaitAndProcessRequestsImpl());
|
||||||
|
@ -175,7 +183,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
protected override void RegisterSessionToWaitList(ServerSession session)
|
protected override void RegisterSessionToWaitList(ServerSession session)
|
||||||
{
|
{
|
||||||
session.HasReceived = false;
|
session.HasReceived = false;
|
||||||
session.UserData = UserDataTag.Session;
|
session.UserData = UserDataTag.Session;
|
||||||
|
|
||||||
RegisterToWaitList(session);
|
RegisterToWaitList(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,15 +207,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
|
|
||||||
private Result Process(MultiWaitHolder holder)
|
private Result Process(MultiWaitHolder holder)
|
||||||
{
|
{
|
||||||
switch ((UserDataTag)holder.UserData)
|
return (UserDataTag)holder.UserData switch
|
||||||
{
|
{
|
||||||
case UserDataTag.Server:
|
UserDataTag.Server => ProcessForServer(holder),
|
||||||
return ProcessForServer(holder);
|
UserDataTag.Session => ProcessForSession(holder),
|
||||||
case UserDataTag.Session:
|
_ => throw new NotImplementedException(((UserDataTag)holder.UserData).ToString())
|
||||||
return ProcessForSession(holder);
|
};
|
||||||
default:
|
|
||||||
throw new NotImplementedException(((UserDataTag)holder.UserData).ToString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result ProcessForServer(MultiWaitHolder holder)
|
private Result ProcessForServer(MultiWaitHolder holder)
|
||||||
|
@ -259,6 +265,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
}
|
}
|
||||||
|
|
||||||
session.HasReceived = true;
|
session.HasReceived = true;
|
||||||
|
|
||||||
tlsMessage.Memory.Span.CopyTo(savedMessage.Memory.Span);
|
tlsMessage.Memory.Span.CopyTo(savedMessage.Memory.Span);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -6,18 +6,18 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
class ServerSession : MultiWaitHolderOfHandle
|
class ServerSession : MultiWaitHolderOfHandle
|
||||||
{
|
{
|
||||||
public ServiceObjectHolder ServiceObjectHolder { get; set; }
|
public ServiceObjectHolder ServiceObjectHolder { get; set; }
|
||||||
public PointerAndSize PointerBuffer { get; set; }
|
public PointerAndSize PointerBuffer { get; set; }
|
||||||
public PointerAndSize SavedMessage { get; set; }
|
public PointerAndSize SavedMessage { get; set; }
|
||||||
public int SessionIndex { get; }
|
public int SessionIndex { get; }
|
||||||
public int SessionHandle { get; }
|
public int SessionHandle { get; }
|
||||||
public bool IsClosed { get; set; }
|
public bool IsClosed { get; set; }
|
||||||
public bool HasReceived { get; set; }
|
public bool HasReceived { get; set; }
|
||||||
|
|
||||||
public ServerSession(int index, int handle, ServiceObjectHolder obj) : base(handle)
|
public ServerSession(int index, int handle, ServiceObjectHolder obj) : base(handle)
|
||||||
{
|
{
|
||||||
ServiceObjectHolder = obj;
|
ServiceObjectHolder = obj;
|
||||||
SessionIndex = index;
|
SessionIndex = index;
|
||||||
SessionHandle = handle;
|
SessionHandle = handle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,9 +75,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
}
|
}
|
||||||
|
|
||||||
session.PointerBuffer = GetSessionPointerBuffer(session);
|
session.PointerBuffer = GetSessionPointerBuffer(session);
|
||||||
session.SavedMessage = GetSessionSavedMessageBuffer(session);
|
session.SavedMessage = GetSessionSavedMessageBuffer(session);
|
||||||
|
|
||||||
RegisterSessionToWaitList(session);
|
RegisterSessionToWaitList(session);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,10 +110,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Server AllocateServer(
|
protected virtual Server AllocateServer(
|
||||||
int portIndex,
|
int portIndex,
|
||||||
int portHandle,
|
int portHandle,
|
||||||
ServiceName name,
|
ServiceName name,
|
||||||
bool managed,
|
bool managed,
|
||||||
ServiceObjectHolder staticHoder)
|
ServiceObjectHolder staticHoder)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
|
@ -141,6 +142,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
protected void CloseSessionImpl(ServerSession session)
|
protected void CloseSessionImpl(ServerSession session)
|
||||||
{
|
{
|
||||||
int sessionHandle = session.Handle;
|
int sessionHandle = session.Handle;
|
||||||
|
|
||||||
Os.FinalizeMultiWaitHolder(session);
|
Os.FinalizeMultiWaitHolder(session);
|
||||||
DestroySession(session);
|
DestroySession(session);
|
||||||
HorizonStatic.Syscall.CloseHandle(sessionHandle).AbortOnFailure();
|
HorizonStatic.Syscall.CloseHandle(sessionHandle).AbortOnFailure();
|
||||||
|
@ -156,6 +158,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
if (session.IsClosed || GetCmifCommandType(message) == CommandType.Close)
|
if (session.IsClosed || GetCmifCommandType(message) == CommandType.Close)
|
||||||
{
|
{
|
||||||
CloseSessionImpl(session);
|
CloseSessionImpl(session);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -165,6 +168,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
if (result.IsSuccess)
|
if (result.IsSuccess)
|
||||||
{
|
{
|
||||||
RegisterSessionToWaitList(session);
|
RegisterSessionToWaitList(session);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
else if (SfResult.RequestContextChanged(result))
|
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}");
|
Logger.Warning?.Print(LogClass.KernelIpc, $"Request processing returned error {result}");
|
||||||
|
|
||||||
CloseSessionImpl(session);
|
CloseSessionImpl(session);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,8 +202,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
return DispatchManagerRequest(session, inMessage, outMessage);
|
return DispatchManagerRequest(session, inMessage, outMessage);
|
||||||
default:
|
default:
|
||||||
return HipcResult.UnknownCommandType;
|
return HipcResult.UnknownCommandType;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static int GetInlineContext(CommandType commandType, ReadOnlySpan<byte> inMessage)
|
private static int GetInlineContext(CommandType commandType, ReadOnlySpan<byte> inMessage)
|
||||||
{
|
{
|
||||||
|
@ -231,7 +236,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
{
|
{
|
||||||
HipcMessageData messageData = HipcMessage.WriteMessage(message, new HipcMetadata()
|
HipcMessageData messageData = HipcMessage.WriteMessage(message, new HipcMetadata()
|
||||||
{
|
{
|
||||||
Type = (int)CommandType.Invalid,
|
Type = (int)CommandType.Invalid,
|
||||||
ReceiveStaticsCount = HipcMessage.AutoReceiveStatic
|
ReceiveStaticsCount = HipcMessage.AutoReceiveStatic
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -271,9 +276,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
|
|
||||||
protected virtual Result DispatchRequest(
|
protected virtual Result DispatchRequest(
|
||||||
ServiceObjectHolder objectHolder,
|
ServiceObjectHolder objectHolder,
|
||||||
ServerSession session,
|
ServerSession session,
|
||||||
Span<byte> inMessage,
|
Span<byte> inMessage,
|
||||||
Span<byte> outMessage)
|
Span<byte> outMessage)
|
||||||
{
|
{
|
||||||
HipcMessage request;
|
HipcMessage request;
|
||||||
|
|
||||||
|
@ -288,14 +293,14 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
|
|
||||||
var dispatchCtx = new ServiceDispatchContext()
|
var dispatchCtx = new ServiceDispatchContext()
|
||||||
{
|
{
|
||||||
ServiceObject = objectHolder.ServiceObject,
|
ServiceObject = objectHolder.ServiceObject,
|
||||||
Manager = this,
|
Manager = this,
|
||||||
Session = session,
|
Session = session,
|
||||||
HandlesToClose = new HandlesToClose(),
|
HandlesToClose = new HandlesToClose(),
|
||||||
PointerBuffer = session.PointerBuffer,
|
PointerBuffer = session.PointerBuffer,
|
||||||
InMessageBuffer = inMessage,
|
InMessageBuffer = inMessage,
|
||||||
OutMessageBuffer = outMessage,
|
OutMessageBuffer = outMessage,
|
||||||
Request = request
|
Request = request
|
||||||
};
|
};
|
||||||
|
|
||||||
ReadOnlySpan<byte> inRawData = MemoryMarshal.Cast<uint, byte>(dispatchCtx.Request.Data.DataWords);
|
ReadOnlySpan<byte> inRawData = MemoryMarshal.Cast<uint, byte>(dispatchCtx.Request.Data.DataWords);
|
||||||
|
@ -332,4 +337,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -136,9 +136,9 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
ulong pointerBufferTail = context.PointerBuffer.Address;
|
ulong pointerBufferTail = context.PointerBuffer.Address;
|
||||||
ulong pointerBufferHead = pointerBufferTail + context.PointerBuffer.Size;
|
ulong pointerBufferHead = pointerBufferTail + context.PointerBuffer.Size;
|
||||||
|
|
||||||
int sendMapAliasIndex = 0;
|
int sendMapAliasIndex = 0;
|
||||||
int recvMapAliasIndex = 0;
|
int recvMapAliasIndex = 0;
|
||||||
int sendPointerIndex = 0;
|
int sendPointerIndex = 0;
|
||||||
int unfixedRecvPointerIndex = 0;
|
int unfixedRecvPointerIndex = 0;
|
||||||
|
|
||||||
for (int i = 0; i < _args.Length; i++)
|
for (int i = 0; i < _args.Length; i++)
|
||||||
|
@ -188,8 +188,9 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
if (flags.HasFlag(HipcBufferFlags.In))
|
if (flags.HasFlag(HipcBufferFlags.In))
|
||||||
{
|
{
|
||||||
var descriptor = context.Request.Data.SendStatics[sendPointerIndex++];
|
var descriptor = context.Request.Data.SendStatics[sendPointerIndex++];
|
||||||
ulong address = descriptor.Address;
|
ulong address = descriptor.Address;
|
||||||
ulong size = descriptor.Size;
|
ulong size = descriptor.Size;
|
||||||
|
|
||||||
_bufferRanges[i] = new PointerAndSize(address, size);
|
_bufferRanges[i] = new PointerAndSize(address, size);
|
||||||
|
|
||||||
if (size != 0)
|
if (size != 0)
|
||||||
|
@ -207,13 +208,14 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var data = MemoryMarshal.Cast<uint, byte>(context.Request.Data.DataWords);
|
var data = MemoryMarshal.Cast<uint, byte>(context.Request.Data.DataWords);
|
||||||
var recvPointerSizes = MemoryMarshal.Cast<byte, ushort>(data.Slice(runtimeMetadata.UnfixedOutPointerSizeOffset));
|
var recvPointerSizes = MemoryMarshal.Cast<byte, ushort>(data[runtimeMetadata.UnfixedOutPointerSizeOffset..]);
|
||||||
|
|
||||||
size = recvPointerSizes[unfixedRecvPointerIndex++];
|
size = recvPointerSizes[unfixedRecvPointerIndex++];
|
||||||
}
|
}
|
||||||
|
|
||||||
pointerBufferHead = BitUtils.AlignDown(pointerBufferHead - size, 0x10UL);
|
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;
|
ref var meta = ref context.Request.Meta;
|
||||||
bool requestValid = true;
|
bool requestValid = true;
|
||||||
requestValid &= meta.SendPid == _hasInProcessIdHolder;
|
requestValid &= meta.SendPid == _hasInProcessIdHolder;
|
||||||
requestValid &= meta.SendStaticsCount == _inPointerBuffersCount;
|
requestValid &= meta.SendStaticsCount == _inPointerBuffersCount;
|
||||||
requestValid &= meta.SendBuffersCount == _inMapAliasBuffersCount;
|
requestValid &= meta.SendBuffersCount == _inMapAliasBuffersCount;
|
||||||
requestValid &= meta.ReceiveBuffersCount == _outMapAliasBuffersCount;
|
requestValid &= meta.ReceiveBuffersCount == _outMapAliasBuffersCount;
|
||||||
requestValid &= meta.ExchangeBuffersCount == 0;
|
requestValid &= meta.ExchangeBuffersCount == 0;
|
||||||
requestValid &= meta.CopyHandlesCount == _inCopyHandlesCount;
|
requestValid &= meta.CopyHandlesCount == _inCopyHandlesCount;
|
||||||
requestValid &= meta.MoveHandlesCount == _inMoveHandlesCount;
|
requestValid &= meta.MoveHandlesCount == _inMoveHandlesCount;
|
||||||
|
|
||||||
int rawSizeInBytes = meta.DataWordsCount * sizeof(uint);
|
int rawSizeInBytes = meta.DataWordsCount * sizeof(uint);
|
||||||
int commandRawSize = BitUtils.AlignUp(runtimeMetadata.UnfixedOutPointerSizeOffset + (OutUnfixedSizePointerBuffersCount * sizeof(ushort)), sizeof(uint));
|
int commandRawSize = BitUtils.AlignUp(runtimeMetadata.UnfixedOutPointerSizeOffset + (OutUnfixedSizePointerBuffersCount * sizeof(ushort)), sizeof(uint));
|
||||||
|
|
||||||
requestValid &= rawSizeInBytes >= commandRawSize;
|
requestValid &= rawSizeInBytes >= commandRawSize;
|
||||||
|
|
||||||
return requestValid ? Result.Success : HipcResult.InvalidCmifRequest;
|
return requestValid ? Result.Success : HipcResult.InvalidCmifRequest;
|
||||||
|
@ -340,7 +343,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
{
|
{
|
||||||
if (_args[i].Type == CommandArgType.InObject)
|
if (_args[i].Type == CommandArgType.InObject)
|
||||||
{
|
{
|
||||||
int index = inObjectIndex++;
|
int index = inObjectIndex++;
|
||||||
var inObject = inObjects[index];
|
var inObject = inObjects[index];
|
||||||
|
|
||||||
objects[index] = inObject?.ServiceObject;
|
objects[index] = inObject?.ServiceObject;
|
||||||
|
@ -365,6 +368,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
_outCopyHandlesCount,
|
_outCopyHandlesCount,
|
||||||
_outMoveHandlesCount + runtimeMetadata.OutObjectsCount);
|
_outMoveHandlesCount + runtimeMetadata.OutObjectsCount);
|
||||||
outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords);
|
outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,6 +381,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
(BitUtils.AlignUp(rawDataSize, 4) + 0x10) / sizeof(uint),
|
(BitUtils.AlignUp(rawDataSize, 4) + 0x10) / sizeof(uint),
|
||||||
0,
|
0,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords);
|
outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,6 +415,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
{
|
{
|
||||||
response.MoveHandles[index] = 0;
|
response.MoveHandles[index] = 0;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,4 +424,4 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
response.MoveHandles[index] = clientHandle;
|
response.MoveHandles[index] = clientHandle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,24 +12,22 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
{
|
{
|
||||||
int argsCount = args.Length;
|
int argsCount = args.Length;
|
||||||
|
|
||||||
int[] sizes = new int[argsCount];
|
int[] sizes = new int[argsCount];
|
||||||
int[] aligns = 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++)
|
for (int i = 0; i < argsCount; i++)
|
||||||
{
|
{
|
||||||
sizes[i] = args[i].ArgSize;
|
sizes[i] = args[i].ArgSize;
|
||||||
aligns[i] = args[i].ArgAlignment;
|
aligns[i] = args[i].ArgAlignment;
|
||||||
map[i] = i;
|
map[i] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 1; i < argsCount; i++)
|
for (int i = 1; i < argsCount; i++)
|
||||||
{
|
{
|
||||||
for (int j = i; j > 0 && aligns[map[j - 1]] > aligns[map[j]]; j--)
|
for (int j = i; j > 0 && aligns[map[j - 1]] > aligns[map[j]]; j--)
|
||||||
{
|
{
|
||||||
var temp = map[j - 1];
|
(map[j], map[j - 1]) = (map[j - 1], map[j]);
|
||||||
map[j - 1] = map[j];
|
|
||||||
map[j] = temp;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,9 +35,9 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
|
|
||||||
foreach (int i in map)
|
foreach (int i in map)
|
||||||
{
|
{
|
||||||
offset = BitUtils.AlignUp(offset, aligns[i]);
|
offset = BitUtils.AlignUp(offset, aligns[i]);
|
||||||
offsets[i] = offset;
|
offsets[i] = offset;
|
||||||
offset += sizes[i];
|
offset += sizes[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
offsets[argsCount] = offset;
|
offsets[argsCount] = offset;
|
||||||
|
@ -48,4 +46,4 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
return offsets;
|
return offsets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,26 +6,22 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
{
|
{
|
||||||
public const int ModuleId = 10;
|
public const int ModuleId = 10;
|
||||||
|
|
||||||
public static Result NotSupported => new Result(ModuleId, 1);
|
public static Result NotSupported => new(ModuleId, 1);
|
||||||
public static Result InvalidHeaderSize => new Result(ModuleId, 202);
|
public static Result InvalidHeaderSize => new(ModuleId, 202);
|
||||||
public static Result InvalidInHeader => new Result(ModuleId, 211);
|
public static Result InvalidInHeader => new(ModuleId, 211);
|
||||||
public static Result InvalidOutHeader => new Result(ModuleId, 212);
|
public static Result InvalidOutHeader => new(ModuleId, 212);
|
||||||
public static Result UnknownCommandId => new Result(ModuleId, 221);
|
public static Result UnknownCommandId => new(ModuleId, 221);
|
||||||
public static Result InvalidOutRawSize => new Result(ModuleId, 232);
|
public static Result InvalidOutRawSize => new(ModuleId, 232);
|
||||||
public static Result InvalidInObjectsCount => new Result(ModuleId, 235);
|
public static Result InvalidInObjectsCount => new(ModuleId, 235);
|
||||||
public static Result InvalidOutObjectsCount => new Result(ModuleId, 236);
|
public static Result InvalidOutObjectsCount => new(ModuleId, 236);
|
||||||
public static Result InvalidInObject => new Result(ModuleId, 239);
|
public static Result InvalidInObject => new(ModuleId, 239);
|
||||||
|
public static Result TargetNotFound => new(ModuleId, 261);
|
||||||
public static Result TargetNotFound => new Result(ModuleId, 261);
|
public static Result OutOfDomainEntries => new(ModuleId, 301);
|
||||||
|
public static Result InvalidatedByUser => new(ModuleId, 802);
|
||||||
public static Result OutOfDomainEntries => new Result(ModuleId, 301);
|
public static Result RequestDeferredByUser => new(ModuleId, 812);
|
||||||
|
|
||||||
public static Result InvalidatedByUser => new Result(ModuleId, 802);
|
|
||||||
public static Result RequestDeferredByUser => new Result(ModuleId, 812);
|
|
||||||
|
|
||||||
public static bool RequestContextChanged(Result result) => result.InRange(800, 899);
|
public static bool RequestContextChanged(Result result) => result.InRange(800, 899);
|
||||||
public static bool Invalidated(Result result) => result.InRange(801, 809);
|
public static bool Invalidated(Result result) => result.InRange(801, 809);
|
||||||
|
public static bool RequestDeferred(Result result) => result.InRange(811, 819);
|
||||||
public static bool RequestDeferred(Result result) => result.InRange(811, 819);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
8
Ryujinx.Horizon/Sdk/Sm/IManagerService.cs
Normal file
8
Ryujinx.Horizon/Sdk/Sm/IManagerService.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
using Ryujinx.Horizon.Sdk.Sf;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Sm
|
||||||
|
{
|
||||||
|
interface IManagerService : IServiceObject
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
13
Ryujinx.Horizon/Sdk/Sm/IUserService.cs
Normal file
13
Ryujinx.Horizon/Sdk/Sm/IUserService.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,16 +7,18 @@ namespace Ryujinx.Horizon.Sdk.Sm
|
||||||
{
|
{
|
||||||
class SmApi
|
class SmApi
|
||||||
{
|
{
|
||||||
|
private const string SmName = "sm:";
|
||||||
|
|
||||||
private int _portHandle;
|
private int _portHandle;
|
||||||
|
|
||||||
public Result Initialize()
|
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)
|
while (result == KernelResult.NotFound)
|
||||||
{
|
{
|
||||||
HorizonStatic.Syscall.SleepThread(50000000L);
|
HorizonStatic.Syscall.SleepThread(50000000L);
|
||||||
result = HorizonStatic.Syscall.ConnectToNamedPort(out portHandle, "sm:");
|
result = HorizonStatic.Syscall.ConnectToNamedPort(out portHandle, SmName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.IsFailure)
|
if (result.IsFailure)
|
||||||
|
@ -33,7 +35,7 @@ namespace Ryujinx.Horizon.Sdk.Sm
|
||||||
{
|
{
|
||||||
Span<byte> data = stackalloc byte[8];
|
Span<byte> data = stackalloc byte[8];
|
||||||
|
|
||||||
SpanWriter writer = new SpanWriter(data);
|
SpanWriter writer = new(data);
|
||||||
|
|
||||||
writer.Write(0UL);
|
writer.Write(0UL);
|
||||||
|
|
||||||
|
@ -44,7 +46,7 @@ namespace Ryujinx.Horizon.Sdk.Sm
|
||||||
{
|
{
|
||||||
Span<byte> data = stackalloc byte[8];
|
Span<byte> data = stackalloc byte[8];
|
||||||
|
|
||||||
SpanWriter writer = new SpanWriter(data);
|
SpanWriter writer = new(data);
|
||||||
|
|
||||||
writer.Write(name);
|
writer.Write(name);
|
||||||
|
|
||||||
|
@ -53,10 +55,12 @@ namespace Ryujinx.Horizon.Sdk.Sm
|
||||||
if (result.IsFailure)
|
if (result.IsFailure)
|
||||||
{
|
{
|
||||||
handle = 0;
|
handle = 0;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle = response.MoveHandles[0];
|
handle = response.MoveHandles[0];
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +68,7 @@ namespace Ryujinx.Horizon.Sdk.Sm
|
||||||
{
|
{
|
||||||
Span<byte> data = stackalloc byte[16];
|
Span<byte> data = stackalloc byte[16];
|
||||||
|
|
||||||
SpanWriter writer = new SpanWriter(data);
|
SpanWriter writer = new(data);
|
||||||
|
|
||||||
writer.Write(name);
|
writer.Write(name);
|
||||||
writer.Write(isLight ? 1 : 0);
|
writer.Write(isLight ? 1 : 0);
|
||||||
|
@ -75,10 +79,12 @@ namespace Ryujinx.Horizon.Sdk.Sm
|
||||||
if (result.IsFailure)
|
if (result.IsFailure)
|
||||||
{
|
{
|
||||||
handle = 0;
|
handle = 0;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle = response.MoveHandles[0];
|
handle = response.MoveHandles[0];
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +92,7 @@ namespace Ryujinx.Horizon.Sdk.Sm
|
||||||
{
|
{
|
||||||
Span<byte> data = stackalloc byte[8];
|
Span<byte> data = stackalloc byte[8];
|
||||||
|
|
||||||
SpanWriter writer = new SpanWriter(data);
|
SpanWriter writer = new(data);
|
||||||
|
|
||||||
writer.Write(name);
|
writer.Write(name);
|
||||||
|
|
||||||
|
@ -97,11 +103,11 @@ namespace Ryujinx.Horizon.Sdk.Sm
|
||||||
{
|
{
|
||||||
Span<byte> data = stackalloc byte[8];
|
Span<byte> data = stackalloc byte[8];
|
||||||
|
|
||||||
SpanWriter writer = new SpanWriter(data);
|
SpanWriter writer = new(data);
|
||||||
|
|
||||||
writer.Write(0UL);
|
writer.Write(0UL);
|
||||||
|
|
||||||
return ServiceUtil.SendRequest(out _, _portHandle, 4, sendPid: true, data);
|
return ServiceUtil.SendRequest(out _, _portHandle, 4, sendPid: true, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,20 +6,22 @@ namespace Ryujinx.Horizon
|
||||||
{
|
{
|
||||||
public struct ServiceEntry
|
public struct ServiceEntry
|
||||||
{
|
{
|
||||||
private readonly Action _entrypoint;
|
private readonly Action<ServiceTable> _entrypoint;
|
||||||
private readonly HorizonOptions _options;
|
private readonly ServiceTable _serviceTable;
|
||||||
|
private readonly HorizonOptions _options;
|
||||||
|
|
||||||
internal ServiceEntry(Action entrypoint, HorizonOptions options)
|
internal ServiceEntry(Action<ServiceTable> entrypoint, ServiceTable serviceTable, HorizonOptions options)
|
||||||
{
|
{
|
||||||
_entrypoint = entrypoint;
|
_entrypoint = entrypoint;
|
||||||
_options = options;
|
_serviceTable = serviceTable;
|
||||||
|
_options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(ISyscallApi syscallApi, IVirtualMemoryManager addressSpace, IThreadContext threadContext)
|
public void Start(ISyscallApi syscallApi, IVirtualMemoryManager addressSpace, IThreadContext threadContext)
|
||||||
{
|
{
|
||||||
HorizonStatic.Register(_options, syscallApi, addressSpace, threadContext, (int)threadContext.GetX(1));
|
HorizonStatic.Register(_options, syscallApi, addressSpace, threadContext, (int)threadContext.GetX(1));
|
||||||
|
|
||||||
_entrypoint();
|
_entrypoint(_serviceTable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,22 +1,58 @@
|
||||||
using Ryujinx.Horizon.LogManager;
|
using Ryujinx.Horizon.LogManager;
|
||||||
|
using Ryujinx.Horizon.Prepo;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Horizon
|
namespace Ryujinx.Horizon
|
||||||
{
|
{
|
||||||
public static class ServiceTable
|
public class ServiceTable
|
||||||
{
|
{
|
||||||
public static IEnumerable<ServiceEntry> GetServices(HorizonOptions options)
|
private int _readyServices;
|
||||||
|
private int _totalServices;
|
||||||
|
|
||||||
|
private readonly ManualResetEvent _servicesReadyEvent = new(false);
|
||||||
|
|
||||||
|
public IEnumerable<ServiceEntry> GetServices(HorizonOptions options)
|
||||||
{
|
{
|
||||||
List<ServiceEntry> entries = new List<ServiceEntry>();
|
List<ServiceEntry> entries = new();
|
||||||
|
|
||||||
void RegisterService<T>() where T : IService
|
void RegisterService<T>() where T : IService
|
||||||
{
|
{
|
||||||
entries.Add(new ServiceEntry(T.Main, options));
|
entries.Add(new ServiceEntry(T.Main, this, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterService<LmMain>();
|
RegisterService<LmMain>();
|
||||||
|
RegisterService<PrepoMain>();
|
||||||
|
|
||||||
|
_totalServices = entries.Count;
|
||||||
|
|
||||||
return entries;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,16 +5,16 @@ namespace Ryujinx.Horizon.Sm.Impl
|
||||||
struct ServiceInfo
|
struct ServiceInfo
|
||||||
{
|
{
|
||||||
public ServiceName Name;
|
public ServiceName Name;
|
||||||
public ulong OwnerProcessId;
|
public ulong OwnerProcessId;
|
||||||
public int PortHandle;
|
public int PortHandle;
|
||||||
|
|
||||||
public void Free()
|
public void Free()
|
||||||
{
|
{
|
||||||
HorizonStatic.Syscall.CloseHandle(PortHandle);
|
HorizonStatic.Syscall.CloseHandle(PortHandle);
|
||||||
|
|
||||||
Name = ServiceName.Invalid;
|
Name = ServiceName.Invalid;
|
||||||
OwnerProcessId = 0L;
|
OwnerProcessId = 0L;
|
||||||
PortHandle = 0;
|
PortHandle = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -107,8 +107,8 @@ namespace Ryujinx.Horizon.Sm.Impl
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
freeService.PortHandle = clientPort;
|
freeService.PortHandle = clientPort;
|
||||||
freeService.Name = name;
|
freeService.Name = name;
|
||||||
freeService.OwnerProcessId = processId;
|
freeService.OwnerProcessId = processId;
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
@ -126,20 +126,19 @@ namespace Ryujinx.Horizon.Sm.Impl
|
||||||
// TODO: Validation with GetProcessInfo etc.
|
// TODO: Validation with GetProcessInfo etc.
|
||||||
|
|
||||||
int serviceIndex = GetServiceInfo(name);
|
int serviceIndex = GetServiceInfo(name);
|
||||||
|
|
||||||
if (serviceIndex < 0)
|
if (serviceIndex < 0)
|
||||||
{
|
{
|
||||||
return SmResult.NotRegistered;
|
return SmResult.NotRegistered;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref var serviceInfo = ref _services[serviceIndex];
|
ref var serviceInfo = ref _services[serviceIndex];
|
||||||
|
|
||||||
if (serviceInfo.OwnerProcessId != processId)
|
if (serviceInfo.OwnerProcessId != processId)
|
||||||
{
|
{
|
||||||
return SmResult.NotAllowed;
|
return SmResult.NotAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceInfo.Free();
|
serviceInfo.Free();
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,4 +193,4 @@ namespace Ryujinx.Horizon.Sm.Impl
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
8
Ryujinx.Horizon/Sm/Ipc/ManagerService.cs
Normal file
8
Ryujinx.Horizon/Sm/Ipc/ManagerService.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
using Ryujinx.Horizon.Sdk.Sm;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sm.Ipc
|
||||||
|
{
|
||||||
|
partial class ManagerService : IManagerService
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,14 +3,14 @@ using Ryujinx.Horizon.Sdk.Sf;
|
||||||
using Ryujinx.Horizon.Sdk.Sm;
|
using Ryujinx.Horizon.Sdk.Sm;
|
||||||
using Ryujinx.Horizon.Sm.Impl;
|
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 readonly ServiceManager _serviceManager;
|
||||||
|
|
||||||
private ulong _clientProcessId;
|
private ulong _clientProcessId;
|
||||||
private bool _initialized;
|
private bool _initialized;
|
||||||
|
|
||||||
public UserService(ServiceManager serviceManager)
|
public UserService(ServiceManager serviceManager)
|
||||||
{
|
{
|
||||||
|
@ -21,7 +21,7 @@ namespace Ryujinx.Horizon.Sm
|
||||||
public Result Initialize([ClientProcessId] ulong clientProcessId)
|
public Result Initialize([ClientProcessId] ulong clientProcessId)
|
||||||
{
|
{
|
||||||
_clientProcessId = clientProcessId;
|
_clientProcessId = clientProcessId;
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
@ -63,4 +63,4 @@ namespace Ryujinx.Horizon.Sm
|
||||||
return _serviceManager.UnregisterService(_clientProcessId, name);
|
return _serviceManager.UnregisterService(_clientProcessId, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
using Ryujinx.Horizon.Sdk.Sf;
|
|
||||||
|
|
||||||
namespace Ryujinx.Horizon.Sm
|
|
||||||
{
|
|
||||||
partial class ManagerService : IServiceObject
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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.Sdk.Sm;
|
||||||
using Ryujinx.Horizon.Sm.Impl;
|
using Ryujinx.Horizon.Sm.Impl;
|
||||||
|
using Ryujinx.Horizon.Sm.Types;
|
||||||
|
|
||||||
namespace Ryujinx.Horizon.Sm
|
namespace Ryujinx.Horizon.Sm
|
||||||
{
|
{
|
||||||
public class SmMain
|
public class SmMain
|
||||||
{
|
{
|
||||||
private enum PortIndex
|
private const int SmMaxSessionsCount = 64;
|
||||||
{
|
private const int SmmMaxSessionsCount = 1;
|
||||||
User,
|
private const int SmTotalMaxSessionsCount = SmMaxSessionsCount + SmmMaxSessionsCount;
|
||||||
Manager
|
|
||||||
}
|
|
||||||
|
|
||||||
private const int MaxPortsCount = 2;
|
private const int MaxPortsCount = 2;
|
||||||
|
|
||||||
private readonly ServerManager _serverManager = new ServerManager(null, null, MaxPortsCount, ManagerOptions.Default, 0);
|
private SmServerManager _serverManager;
|
||||||
private readonly ServiceManager _serviceManager = new ServiceManager();
|
|
||||||
|
private readonly ServiceManager _serviceManager = new();
|
||||||
|
|
||||||
public void Main()
|
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);
|
_serverManager = new SmServerManager(_serviceManager, null, null, MaxPortsCount, ManagerOptions.Default, SmTotalMaxSessionsCount);
|
||||||
_serviceManager.RegisterServiceForSelf(out int smmHandle, ServiceName.Encode("sm:m"), 1).AbortOnFailure();
|
|
||||||
_serverManager.RegisterServer((int)PortIndex.Manager, smmHandle);
|
_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();
|
_serverManager.ServiceRequests();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,14 +6,14 @@ namespace Ryujinx.Horizon.Sm
|
||||||
{
|
{
|
||||||
private const int ModuleId = 21;
|
private const int ModuleId = 21;
|
||||||
|
|
||||||
public static Result OutOfProcess => new Result(ModuleId, 1);
|
public static Result OutOfProcess => new(ModuleId, 1);
|
||||||
public static Result InvalidClient => new Result(ModuleId, 2);
|
public static Result InvalidClient => new(ModuleId, 2);
|
||||||
public static Result OutOfSessions => new Result(ModuleId, 3);
|
public static Result OutOfSessions => new(ModuleId, 3);
|
||||||
public static Result AlreadyRegistered => new Result(ModuleId, 4);
|
public static Result AlreadyRegistered => new(ModuleId, 4);
|
||||||
public static Result OutOfServices => new Result(ModuleId, 5);
|
public static Result OutOfServices => new(ModuleId, 5);
|
||||||
public static Result InvalidServiceName => new Result(ModuleId, 6);
|
public static Result InvalidServiceName => new(ModuleId, 6);
|
||||||
public static Result NotRegistered => new Result(ModuleId, 7);
|
public static Result NotRegistered => new(ModuleId, 7);
|
||||||
public static Result NotAllowed => new Result(ModuleId, 8);
|
public static Result NotAllowed => new(ModuleId, 8);
|
||||||
public static Result TooLargeAccessControl => new Result(ModuleId, 9);
|
public static Result TooLargeAccessControl => new(ModuleId, 9);
|
||||||
}
|
}
|
||||||
}
|
}
|
30
Ryujinx.Horizon/Sm/SmServerManager.cs
Normal file
30
Ryujinx.Horizon/Sm/SmServerManager.cs
Normal file
|
@ -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)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
Ryujinx.Horizon/Sm/Types/SmPortIndex.cs
Normal file
8
Ryujinx.Horizon/Sm/Types/SmPortIndex.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Ryujinx.Horizon.Sm.Types
|
||||||
|
{
|
||||||
|
enum SmPortIndex
|
||||||
|
{
|
||||||
|
User,
|
||||||
|
Manager
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue