Skip to content

Commit

Permalink
Horizon: Implement arp:r and arp:w services (#5802)
Browse files Browse the repository at this point in the history
* Horizon: Implement arp:r and arp:w services

* Fix formatting

* Remove HLE arp services

* Revert "Remove HLE arp services"

This reverts commit c576fcccadb963db56b96bacabd1c1ac7abfb1ab.

* Keep LibHac impl since it's used in bcat

* Addresses gdkchan's feedback

* ArpApi in PrepoIpcServer and remove LmApi

* Fix 2

* Fixes ArpApi init

* Fix encoding

* Update PrepoService.cs

* Fix prepo
  • Loading branch information
AcK77 authored Jan 25, 2024
1 parent 43705c2 commit cd37c75
Show file tree
Hide file tree
Showing 40 changed files with 1,415 additions and 37 deletions.
11 changes: 11 additions & 0 deletions src/Ryujinx.Common/Memory/StructArrayHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,17 @@ public struct Array64<T> : IArray<T> where T : unmanaged
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
}

public struct Array65<T> : IArray<T> where T : unmanaged
{
T _e0;
Array64<T> _other;
public readonly int Length => 65;
public ref T this[int index] => ref AsSpan()[index];

[Pure]
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
}

public struct Array73<T> : IArray<T> where T : unmanaged
{
T _e0;
Expand Down
12 changes: 12 additions & 0 deletions src/Ryujinx.Common/Memory/StructByteArrayHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ public struct ByteArray2048 : IArray<byte>
public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size);
}

[StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)]
public struct ByteArray3000 : IArray<byte>
{
private const int Size = 3000;

byte _element;

public readonly int Length => Size;
public ref byte this[int index] => ref AsSpan()[index];
public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref _element, Size);
}

[StructLayout(LayoutKind.Sequential, Size = Size, Pack = 1)]
public struct ByteArray4096 : IArray<byte>
{
Expand Down
8 changes: 0 additions & 8 deletions src/Ryujinx.HLE/HOS/Services/Arp/IReader.cs

This file was deleted.

8 changes: 0 additions & 8 deletions src/Ryujinx.HLE/HOS/Services/Arp/IWriter.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,7 @@ public ResultCode ConfirmStereoVisionRestrictionConfigurable(ServiceCtx context)
}
else
{
#pragma warning disable CS0162 // Unreachable code
return ResultCode.StereoVisionRestrictionConfigurableDisabled;
#pragma warning restore CS0162
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public static MetaLoader GetNpdm(this IFileSystem fileSystem)
return metaLoader;
}

public static ProcessResult Load(this IFileSystem exeFs, Switch device, BlitStruct<ApplicationControlProperty> nacpData, MetaLoader metaLoader, bool isHomebrew = false)
public static ProcessResult Load(this IFileSystem exeFs, Switch device, BlitStruct<ApplicationControlProperty> nacpData, MetaLoader metaLoader, byte programIndex, bool isHomebrew = false)
{
ulong programId = metaLoader.GetProgramId();

Expand Down Expand Up @@ -119,6 +119,7 @@ public static ProcessResult Load(this IFileSystem exeFs, Switch device, BlitStru
true,
programName,
metaLoader.GetProgramId(),
programIndex,
null,
nsoExecutables);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static ProcessResult Load(this LocalFileSystem exeFs, Switch device, stri
ProcessLoaderHelper.EnsureSaveData(device, new ApplicationId(programId), nacpData);
}

ProcessResult processResult = exeFs.Load(device, nacpData, metaLoader);
ProcessResult processResult = exeFs.Load(device, nacpData, metaLoader, 0);

// Load RomFS.
if (!string.IsNullOrEmpty(romFsPath))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca
*/

ProcessResult processResult = exeFs.Load(device, nacpData, metaLoader);
ProcessResult processResult = exeFs.Load(device, nacpData, metaLoader, (byte)nca.GetProgramIndex());

// Load RomFS.
if (romFs == null)
Expand Down
5 changes: 3 additions & 2 deletions src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public bool LoadNsp(string path)
if (processResult.ProcessId == 0)
{
// This is not a normal NSP, it's actually a ExeFS as a NSP
processResult = partitionFileSystem.Load(_device, new BlitStruct<ApplicationControlProperty>(1), partitionFileSystem.GetNpdm(), true);
processResult = partitionFileSystem.Load(_device, new BlitStruct<ApplicationControlProperty>(1), partitionFileSystem.GetNpdm(), 0, true);
}

if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
Expand Down Expand Up @@ -198,7 +198,7 @@ public bool LoadNxo(string path)
}
else
{
programName = System.IO.Path.GetFileNameWithoutExtension(path);
programName = Path.GetFileNameWithoutExtension(path);

executable = new NsoExecutable(new LocalStorage(path, FileAccess.Read), programName);
}
Expand All @@ -215,6 +215,7 @@ public bool LoadNxo(string path)
allowCodeMemoryForJit: true,
programName,
programId,
0,
null,
executable);

Expand Down
23 changes: 22 additions & 1 deletion src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Loaders.Processes.Extensions;
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Arp;
using System;
using System.Linq;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -229,6 +230,7 @@ public static ProcessResult LoadNsos(
bool allowCodeMemoryForJit,
string name,
ulong programId,
byte programIndex,
byte[] arguments = null,
params IExecutable[] executables)
{
Expand Down Expand Up @@ -421,7 +423,7 @@ public static ProcessResult LoadNsos(
// Once everything is loaded, we can load cheats.
device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(programId, tamperInfo, device.TamperMachine);

return new ProcessResult(
ProcessResult processResult = new(
metaLoader,
applicationControlProperties,
diskCacheEnabled,
Expand All @@ -431,6 +433,25 @@ public static ProcessResult LoadNsos(
meta.MainThreadPriority,
meta.MainThreadStackSize,
device.System.State.DesiredTitleLanguage);

// Register everything in arp service.
device.System.ServiceTable.ArpWriter.AcquireRegistrar(out IRegistrar registrar);
registrar.SetApplicationControlProperty(MemoryMarshal.Cast<byte, Horizon.Sdk.Ns.ApplicationControlProperty>(applicationControlProperties.ByteSpan)[0]);
// TODO: Handle Version and StorageId when it will be needed.
registrar.SetApplicationLaunchProperty(new ApplicationLaunchProperty()
{
ApplicationId = new Horizon.Sdk.Ncm.ApplicationId(programId),
Version = 0x00,
Storage = Horizon.Sdk.Ncm.StorageId.BuiltInUser,
PatchStorage = Horizon.Sdk.Ncm.StorageId.None,
ApplicationKind = ApplicationKind.Application,
});

device.System.ServiceTable.ArpReader.GetApplicationInstanceId(out ulong applicationInstanceId, process.Pid);
device.System.ServiceTable.ArpWriter.AcquireApplicationProcessPropertyUpdater(out IUpdater updater, applicationInstanceId);
updater.SetApplicationProcessProperty(process.Pid, new ApplicationProcessProperty() { ProgramIndex = programIndex });

return processResult;
}

public static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress)
Expand Down
61 changes: 61 additions & 0 deletions src/Ryujinx.Horizon/Arp/ArpIpcServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Ryujinx.Horizon.Arp.Ipc;
using Ryujinx.Horizon.Sdk.Arp;
using Ryujinx.Horizon.Sdk.Arp.Detail;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using Ryujinx.Horizon.Sdk.Sm;

namespace Ryujinx.Horizon.Arp
{
class ArpIpcServer
{
private const int ArpRMaxSessionsCount = 16;
private const int ArpWMaxSessionsCount = 8;
private const int MaxSessionsCount = ArpRMaxSessionsCount + ArpWMaxSessionsCount;

private const int PointerBufferSize = 0x1000;
private const int MaxDomains = 24;
private const int MaxDomainObjects = 32;
private const int MaxPortsCount = 2;

private static readonly ManagerOptions _managerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);

private SmApi _sm;
private ServerManager _serverManager;
private ApplicationInstanceManager _applicationInstanceManager;

public IReader Reader { get; private set; }
public IWriter Writer { get; private set; }

public void Initialize()
{
HeapAllocator allocator = new();

_sm = new SmApi();
_sm.Initialize().AbortOnFailure();

_serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _managerOptions, MaxSessionsCount);

_applicationInstanceManager = new ApplicationInstanceManager();

Reader reader = new(_applicationInstanceManager);
Reader = reader;

Writer writer = new(_applicationInstanceManager);
Writer = writer;

_serverManager.RegisterObjectForServer(reader, ServiceName.Encode("arp:r"), ArpRMaxSessionsCount);
_serverManager.RegisterObjectForServer(writer, ServiceName.Encode("arp:w"), ArpWMaxSessionsCount);
}

public void ServiceRequests()
{
_serverManager.ServiceRequests();
}

public void Shutdown()
{
_applicationInstanceManager.Dispose();
_serverManager.Dispose();
}
}
}
20 changes: 20 additions & 0 deletions src/Ryujinx.Horizon/Arp/ArpMain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Ryujinx.Horizon.Arp
{
class ArpMain : IService
{
public static void Main(ServiceTable serviceTable)
{
ArpIpcServer arpIpcServer = new();

arpIpcServer.Initialize();

serviceTable.ArpReader = arpIpcServer.Reader;
serviceTable.ArpWriter = arpIpcServer.Writer;

serviceTable.SignalServiceReady();

arpIpcServer.ServiceRequests();
arpIpcServer.Shutdown();
}
}
}
135 changes: 135 additions & 0 deletions src/Ryujinx.Horizon/Arp/Ipc/Reader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Arp;
using Ryujinx.Horizon.Sdk.Arp.Detail;
using Ryujinx.Horizon.Sdk.Ns;
using Ryujinx.Horizon.Sdk.Sf;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using System;

namespace Ryujinx.Horizon.Arp.Ipc
{
partial class Reader : IReader, IServiceObject
{
private readonly ApplicationInstanceManager _applicationInstanceManager;

public Reader(ApplicationInstanceManager applicationInstanceManager)
{
_applicationInstanceManager = applicationInstanceManager;
}

[CmifCommand(0)]
public Result GetApplicationLaunchProperty(out ApplicationLaunchProperty applicationLaunchProperty, ulong applicationInstanceId)
{
if (_applicationInstanceManager.Entries[applicationInstanceId] == null || !_applicationInstanceManager.Entries[applicationInstanceId].LaunchProperty.HasValue)
{
applicationLaunchProperty = default;

return ArpResult.InvalidInstanceId;
}

applicationLaunchProperty = _applicationInstanceManager.Entries[applicationInstanceId].LaunchProperty.Value;

return Result.Success;
}

[CmifCommand(1)]
public Result GetApplicationControlProperty([Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias, 0x4000)] out ApplicationControlProperty applicationControlProperty, ulong applicationInstanceId)
{
if (_applicationInstanceManager.Entries[applicationInstanceId] == null || !_applicationInstanceManager.Entries[applicationInstanceId].ControlProperty.HasValue)
{
applicationControlProperty = default;

return ArpResult.InvalidInstanceId;
}

applicationControlProperty = _applicationInstanceManager.Entries[applicationInstanceId].ControlProperty.Value;

return Result.Success;
}

[CmifCommand(2)]
public Result GetApplicationProcessProperty(out ApplicationProcessProperty applicationProcessProperty, ulong applicationInstanceId)
{
if (_applicationInstanceManager.Entries[applicationInstanceId] == null || !_applicationInstanceManager.Entries[applicationInstanceId].ProcessProperty.HasValue)
{
applicationProcessProperty = default;

return ArpResult.InvalidInstanceId;
}

applicationProcessProperty = _applicationInstanceManager.Entries[applicationInstanceId].ProcessProperty.Value;

return Result.Success;
}

[CmifCommand(3)]
public Result GetApplicationInstanceId(out ulong applicationInstanceId, ulong pid)
{
applicationInstanceId = 0;

if (pid == 0)
{
return ArpResult.InvalidPid;
}

for (int i = 0; i < _applicationInstanceManager.Entries.Length; i++)
{
if (_applicationInstanceManager.Entries[i] != null && _applicationInstanceManager.Entries[i].Pid == pid)
{
applicationInstanceId = (ulong)i;

return Result.Success;
}
}

return ArpResult.InvalidPid;
}

[CmifCommand(4)]
public Result GetApplicationInstanceUnregistrationNotifier(out IUnregistrationNotifier unregistrationNotifier)
{
unregistrationNotifier = new UnregistrationNotifier(_applicationInstanceManager);

return Result.Success;
}

[CmifCommand(5)]
public Result ListApplicationInstanceId(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<ulong> applicationInstanceIdList)
{
count = 0;

if (_applicationInstanceManager.Entries[0] != null)
{
applicationInstanceIdList[count++] = 0;
}

if (_applicationInstanceManager.Entries[1] != null)
{
applicationInstanceIdList[count++] = 1;
}

return Result.Success;
}

[CmifCommand(6)]
public Result GetMicroApplicationInstanceId(out ulong microApplicationInstanceId, [ClientProcessId] ulong pid)
{
return GetApplicationInstanceId(out microApplicationInstanceId, pid);
}

[CmifCommand(7)]
public Result GetApplicationCertificate([Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias | HipcBufferFlags.FixedSize, 0x528)] out ApplicationCertificate applicationCertificate, ulong applicationInstanceId)
{
if (_applicationInstanceManager.Entries[applicationInstanceId] == null)
{
applicationCertificate = default;

return ArpResult.InvalidInstanceId;
}

applicationCertificate = _applicationInstanceManager.Entries[applicationInstanceId].Certificate.Value;

return Result.Success;
}
}
}
Loading

0 comments on commit cd37c75

Please sign in to comment.