Implement the remaining gcsrv functions

This commit is contained in:
Alex Barney 2022-05-24 16:35:30 -07:00
parent e18f6a9daf
commit 56765598a7
9 changed files with 401 additions and 31 deletions

View file

@ -3,6 +3,7 @@ using LibHac.Common.Keys;
using LibHac.FsSrv.FsCreator;
using LibHac.FsSrv.Storage;
using LibHac.FsSystem;
using LibHac.Gc;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
namespace LibHac.FsSrv;
@ -12,6 +13,7 @@ public class DefaultFsServerObjects
public FileSystemCreatorInterfaces FsCreators { get; set; }
public EmulatedGameCard GameCard { get; set; }
public EmulatedSdCard SdCard { get; set; }
public GameCardDummy GameCardNew { get; set; }
public EmulatedStorageDeviceManagerFactory StorageDeviceManagerFactory { get; set; }
public static DefaultFsServerObjects GetDefaultEmulatedCreators(IFileSystem rootFileSystem, KeySet keySet,
@ -21,6 +23,8 @@ public class DefaultFsServerObjects
var gameCard = new EmulatedGameCard(keySet);
var sdCard = new EmulatedSdCard();
var gameCardNew = new GameCardDummy();
var gcStorageCreator = new EmulatedGameCardStorageCreator(gameCard);
using var sharedRootFileSystem = new SharedRef<IFileSystem>(rootFileSystem);
@ -39,13 +43,14 @@ public class DefaultFsServerObjects
creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(ref sharedRootFileSystem.Ref);
creators.SdCardFileSystemCreator = new EmulatedSdCardFileSystemCreator(sdCard, ref sharedRootFileSystemCopy.Ref);
var storageDeviceManagerFactory = new EmulatedStorageDeviceManagerFactory(fsServer, true);
var storageDeviceManagerFactory = new EmulatedStorageDeviceManagerFactory(fsServer, gameCardNew, hasGameCard: true);
return new DefaultFsServerObjects
{
FsCreators = creators,
GameCard = gameCard,
SdCard = sdCard,
GameCardNew = gameCardNew,
StorageDeviceManagerFactory = storageDeviceManagerFactory
};
}

View file

@ -25,9 +25,10 @@ public class EmulatedStorageDeviceManagerFactory : IStorageDeviceManagerFactory
private readonly FileSystemServer _fsServer;
private readonly GameCardDummy _gc;
public EmulatedStorageDeviceManagerFactory(FileSystemServer fsServer, bool hasGameCard)
public EmulatedStorageDeviceManagerFactory(FileSystemServer fsServer, GameCardDummy gc, bool hasGameCard)
{
_fsServer = fsServer;
_gc = gc;
_hasGameCard = hasGameCard;
_gameCardDeviceMutex = new SdkMutexType();
@ -110,12 +111,80 @@ public class EmulatedStorageDeviceManagerFactory : IStorageDeviceManagerFactory
return ResultFs.StorageDeviceInvalidOperation.Log();
}
public void AwakenAll()
{
EnsureMmcReady();
_mmcDeviceManager.Get.Awaken().IgnoreResult();
using (ScopedLock.Lock(ref _sdCardDeviceMutex))
{
if (_sdCardDeviceManager.HasValue)
{
_sdCardDeviceManager.Get.Awaken().IgnoreResult();
}
}
using (ScopedLock.Lock(ref _gameCardDeviceMutex))
{
if (_gameCardDeviceManager.HasValue)
{
_gameCardDeviceManager.Get.Awaken().IgnoreResult();
}
}
}
public void PutAllToSleep()
{
using (ScopedLock.Lock(ref _gameCardDeviceMutex))
{
if (_gameCardDeviceManager.HasValue)
{
_gameCardDeviceManager.Get.PutToSleep().IgnoreResult();
}
}
using (ScopedLock.Lock(ref _sdCardDeviceMutex))
{
if (_sdCardDeviceManager.HasValue)
{
_sdCardDeviceManager.Get.PutToSleep().IgnoreResult();
}
}
EnsureMmcReady();
_mmcDeviceManager.Get.PutToSleep().IgnoreResult();
}
public void ShutdownAll()
{
using (ScopedLock.Lock(ref _gameCardDeviceMutex))
{
if (_gameCardDeviceManager.HasValue)
{
_gameCardDeviceManager.Get.Shutdown().IgnoreResult();
}
}
using (ScopedLock.Lock(ref _sdCardDeviceMutex))
{
if (_sdCardDeviceManager.HasValue)
{
_sdCardDeviceManager.Get.Shutdown().IgnoreResult();
}
}
EnsureMmcReady();
_mmcDeviceManager.Get.Shutdown().IgnoreResult();
}
private void EnsureMmcReady()
{
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mmcDeviceMutex);
if (!_mmcDeviceManager.HasValue)
{
// Missing: Register device address space
_mmcDeviceManager.Reset(new MmcManager());
}
}

View file

@ -1,12 +1,18 @@
using System;
using System.Collections.Generic;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.FsSrv.Sf;
using LibHac.Os;
using LibHac.Sf;
namespace LibHac.FsSystem;
/// <summary>
/// Base class for classes that manage registering events and signaling them when a card device is inserted or removed.
/// </summary>
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
internal class CardDeviceDetectionEventManager : IDisposable
{
private LinkedList<CardDeviceDetectionEvent> _events;
@ -28,29 +34,55 @@ internal class CardDeviceDetectionEventManager : IDisposable
CallbackArgs = new CallbackArguments { EventManager = this, Mutex = _mutex };
}
public virtual void Dispose()
{
throw new NotImplementedException();
}
public virtual void Dispose() { }
public Result CreateDetectionEvent(ref SharedRef<IEventNotifier> outDetectionEvent)
{
throw new NotImplementedException();
using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
var detectionEventImpl = new CardDeviceDetectionEvent(this);
using var detectionEvent = new SharedRef<IEventNotifier>(detectionEventImpl);
if (!detectionEvent.HasValue)
return ResultFs.AllocationMemoryFailedInDeviceDetectionEventManagerA.Log();
_events.AddLast(detectionEventImpl);
outDetectionEvent.SetByMove(ref detectionEvent.Ref );
return Result.Success;
}
public void Unlink(CardDeviceDetectionEvent detectionEvent)
{
throw new NotImplementedException();
using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
_events.Remove(detectionEvent);
}
public void SignalAll()
{
throw new NotImplementedException();
using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
// ReSharper disable once UnusedVariable
foreach (CardDeviceDetectionEvent detectionEvent in _events)
{
// Todo: Signal event
}
}
protected static void DetectionEventCallback(object args)
{
throw new NotImplementedException();
Abort.DoAbortUnless(args is CallbackArguments, "Invalid device detection callback arguments type.");
var callbackArgs = (CallbackArguments)args;
using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref callbackArgs.Mutex);
// ReSharper disable once UnusedVariable
foreach (CardDeviceDetectionEvent detectionEvent in callbackArgs.EventManager._events)
{
// Todo: Signal event
}
}
}
@ -66,7 +98,8 @@ internal class CardDeviceDetectionEvent : IEventNotifier
public void Dispose()
{
throw new NotImplementedException();
_eventManager?.Unlink(this);
_eventManager = null;
}
public Result GetEventHandle(out NativeHandle handle)

View file

@ -1,17 +1,27 @@
using System;
using LibHac.FsSystem;
using LibHac.FsSystem;
using LibHac.Gc;
namespace LibHac.GcSrv;
/// <summary>
/// Manages registering events and signaling them when a game card is inserted or removed.
/// </summary>
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
internal class GameCardDetectionEventManager : CardDeviceDetectionEventManager
{
public GameCardDetectionEventManager()
private GameCardDummy _gc;
public GameCardDetectionEventManager(GameCardDummy gc)
{
throw new NotImplementedException();
_gc = gc;
gc.RegisterDetectionEventCallback(DetectionEventCallback, CallbackArgs);
}
public override void Dispose()
{
throw new NotImplementedException();
_gc.UnregisterDetectionEventCallback();
base.Dispose();
}
}

View file

@ -14,7 +14,8 @@ using LibHac.Os;
using LibHac.Sf;
using static LibHac.Gc.Values;
using static LibHac.GcSrv.GameCardDeviceOperator;
using IStorage = LibHac.FsSrv.Sf.IStorage;
using IStorage = LibHac.Fs.IStorage;
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
namespace LibHac.GcSrv;
@ -124,7 +125,7 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
return Result.Success;
}
public Result InitializeGcLibrary()
private Result InitializeGcLibrary()
{
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
@ -139,7 +140,7 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
_gc.Initialize(default, default);
// Missing: Register the device buffer
_detectionEventManager = new GameCardDetectionEventManager();
_detectionEventManager = new GameCardDetectionEventManager(_gc);
_isInitialized = true;
return Result.Success;
@ -358,7 +359,7 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
return Result.Success;
}
public Result OpenStorage(ref SharedRef<IStorage> outStorage, ulong attribute)
public Result OpenStorage(ref SharedRef<IStorageSf> outStorage, ulong attribute)
{
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
@ -375,7 +376,208 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
private Result OpenDeviceImpl(ref SharedRef<IStorageDevice> outStorageDevice, OpenGameCardAttribute attribute)
{
throw new NotImplementedException();
Result res;
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
using var storageDevice = new SharedRef<IStorageDevice>();
using var baseStorage = new SharedRef<IStorage>();
switch (attribute)
{
case OpenGameCardAttribute.ReadOnly:
res = OpenDeviceReadOnly(ref baseStorage.Ref, ref storageDevice.Ref);
if (res.IsFailure()) return res.Miss();
break;
case OpenGameCardAttribute.SecureReadOnly:
res = OpenDeviceSecureReadOnly(ref baseStorage.Ref, ref storageDevice.Ref);
if (res.IsFailure()) return res.Miss();
break;
case OpenGameCardAttribute.WriteOnly:
res = OpenDeviceWriteOnly(ref baseStorage.Ref, ref storageDevice.Ref);
if (res.IsFailure()) return res.Miss();
break;
default:
return ResultFs.InvalidArgument.Log();
}
outStorageDevice.SetByMove(ref storageDevice.Ref);
return Result.Success;
}
private Result OpenDeviceReadOnly(ref SharedRef<IStorage> outStorage,
ref SharedRef<IStorageDevice> outStorageDevice)
{
Result res = EnsureGameCardNormalMode(out GameCardHandle handle);
if (res.IsFailure()) return res.Miss();
res = CreateReadOnlyStorage(ref outStorage);
if (res.IsFailure()) return res.Miss();
if (!outStorage.HasValue)
return ResultFs.AllocationMemoryFailedInGameCardManagerB.Log();
using SharedRef<IStorageDevice> storageDevice = CreateStorageDeviceNonSecure(in outStorage, handle);
if (!storageDevice.HasValue)
return ResultFs.AllocationMemoryFailedInGameCardManagerB.Log();
outStorageDevice.SetByMove(ref storageDevice.Ref);
return Result.Success;
}
private Result OpenDeviceSecureReadOnly(ref SharedRef<IStorage> outStorage,
ref SharedRef<IStorageDevice> outStorageDevice)
{
Result res = EnsureGameCardSecureMode(out GameCardHandle handle);
if (res.IsFailure()) return res.Miss();
Span<byte> currentCardDeviceId = stackalloc byte[GcCardDeviceIdSize];
Span<byte> currentCardImageHash = stackalloc byte[GcCardImageHashSize];
res = HandleGameCardAccessResult(_gc.GetCardDeviceId(currentCardDeviceId));
if (res.IsFailure()) return res.Miss();
res = HandleGameCardAccessResult(_gc.GetCardImageHash(currentCardImageHash));
if (res.IsFailure()) return res.Miss();
res = CreateSecureReadOnlyStorage(ref outStorage);
if (res.IsFailure()) return res.Miss();
if (!outStorage.HasValue)
return ResultFs.AllocationMemoryFailedInGameCardManagerC.Log();
using SharedRef<IStorageDevice> storageDevice =
CreateStorageDeviceSecure(in outStorage, handle, currentCardDeviceId, currentCardImageHash);
if (!storageDevice.HasValue)
return ResultFs.AllocationMemoryFailedInGameCardManagerD.Log();
outStorageDevice.SetByMove(ref storageDevice.Ref);
return Result.Success;
}
private Result OpenDeviceWriteOnly(ref SharedRef<IStorage> outStorage,
ref SharedRef<IStorageDevice> outStorageDevice)
{
Result res = EnsureGameCardWriteMode(out GameCardHandle handle);
if (res.IsFailure()) return res.Miss();
res = CreateWriteOnlyStorage(ref outStorage);
if (res.IsFailure()) return res.Miss();
if (!outStorage.HasValue)
return ResultFs.AllocationMemoryFailedInGameCardManagerE.Log();
using SharedRef<IStorageDevice> storageDevice = CreateStorageDeviceNonSecure(in outStorage, handle);
if (!storageDevice.HasValue)
return ResultFs.AllocationMemoryFailedInGameCardManagerF.Log();
outStorageDevice.SetByMove(ref storageDevice.Ref);
return Result.Success;
}
private class DelegatedSubStorage : SubStorage
{
private SharedRef<IStorage> _baseStorageShared;
public DelegatedSubStorage(ref UniqueRef<IStorage> baseStorage, long offset, long size)
: base(baseStorage.Get, offset, size)
{
_baseStorageShared = SharedRef<IStorage>.Create(ref baseStorage);
}
public override void Dispose()
{
_baseStorageShared.Destroy();
base.Dispose();
}
}
private Result CreateReadOnlyStorage(ref SharedRef<IStorage> outStorage)
{
using var storage = new UniqueRef<IStorage>(MakeReadOnlyGameCardStorage());
if (!storage.HasValue)
return ResultFs.AllocationMemoryFailedInGameCardManagerA.Log();
Result res = HandleGameCardAccessResult(_gc.GetCardStatus(out GameCardStatus cardStatus));
if (res.IsFailure()) return res.Miss();
long size = cardStatus.SecureAreaOffset;
outStorage.Reset(new DelegatedSubStorage(ref storage.Ref, 0, size));
return Result.Success;
}
private Result CreateSecureReadOnlyStorage(ref SharedRef<IStorage> outStorage)
{
using var storage = new UniqueRef<IStorage>(MakeReadOnlyGameCardStorage());
if (!storage.HasValue)
return ResultFs.AllocationMemoryFailedInGameCardManagerC.Log();
Result res = HandleGameCardAccessResult(_gc.GetCardStatus(out GameCardStatus cardStatus));
if (res.IsFailure()) return res.Miss();
long offset = cardStatus.SecureAreaOffset;
long size = cardStatus.SecureAreaSize;
outStorage.Reset(new DelegatedSubStorage(ref storage.Ref, offset, size));
return Result.Success;
}
private Result CreateWriteOnlyStorage(ref SharedRef<IStorage> outStorage)
{
using var storage = new UniqueRef<IStorage>(MakeWriteOnlyGameCardStorage());
if (!storage.HasValue)
return ResultFs.AllocationMemoryFailedInGameCardManagerE.Log();
Result res = storage.Get.GetSize(out long size);
if (res.IsFailure()) return res.Miss();
outStorage.Reset(new DelegatedSubStorage(ref storage.Ref, 0, size));
return Result.Success;
}
private ReadOnlyGameCardStorage MakeReadOnlyGameCardStorage()
{
using SharedRef<IGameCardManager> manager = SharedRef<IGameCardManager>.Create(in _selfReference);
return new ReadOnlyGameCardStorage(ref manager.Ref, _gc);
}
private WriteOnlyGameCardStorage MakeWriteOnlyGameCardStorage()
{
using SharedRef<IGameCardManager> manager = SharedRef<IGameCardManager>.Create(in _selfReference);
return new WriteOnlyGameCardStorage(ref manager.Ref, _gc);
}
private SharedRef<IStorageDevice> CreateStorageDeviceNonSecure(in SharedRef<IStorage> baseStorage,
GameCardHandle handle)
{
using SharedRef<IGameCardManager> manager = SharedRef<IGameCardManager>.Create(in _selfReference);
using SharedRef<GameCardStorageDevice> storageDevice =
GameCardStorageDevice.CreateShared(_gc, ref manager.Ref, in baseStorage, handle);
return SharedRef<IStorageDevice>.CreateMove(ref storageDevice.Ref);
}
private SharedRef<IStorageDevice> CreateStorageDeviceSecure(in SharedRef<IStorage> baseStorage,
GameCardHandle handle, ReadOnlySpan<byte> cardDeviceId, ReadOnlySpan<byte> cardImageHash)
{
using SharedRef<IGameCardManager> manager = SharedRef<IGameCardManager>.Create(in _selfReference);
using SharedRef<GameCardStorageDevice> storageDevice = GameCardStorageDevice.CreateShared(
_gc, ref manager.Ref, in baseStorage, handle, isSecure: true, cardDeviceId, cardImageHash);
return SharedRef<IStorageDevice>.CreateMove(ref storageDevice.Ref);
}
public Result PutToSleep()
@ -956,6 +1158,15 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
public void PresetInternalKeys(ReadOnlySpan<byte> gameCardKey, ReadOnlySpan<byte> gameCardCertificate)
{
throw new NotImplementedException();
if (gameCardKey.IsEmpty || gameCardCertificate.IsEmpty)
{
_fsServer.Hos.Diag.Impl.LogImpl(Log.EmptyModuleName, LogSeverity.Info, "[fs] Warning: skipped nn::gc::PresetInternalKeys\n"u8);
}
else
{
_gc.PresetInternalKeys(gameCardKey, gameCardCertificate);
}
// Missing: Signal settings-ready event
}
}

View file

@ -11,6 +11,10 @@ using IStorageSf = LibHac.FsSrv.Sf.IStorage;
namespace LibHac.GcSrv;
/// <summary>
/// Provides an <see cref="IStorage"/> interface for reading from the game card.
/// </summary>
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
internal class ReadOnlyGameCardStorage : IStorage
{
private SharedRef<IGameCardManager> _deviceManager;
@ -90,6 +94,10 @@ internal class ReadOnlyGameCardStorage : IStorage
}
}
/// <summary>
/// Provides an <see cref="IStorage"/> interface for writing to the game card.
/// </summary>
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
internal class WriteOnlyGameCardStorage : IStorage
{
private SharedRef<IGameCardManager> _deviceManager;
@ -156,13 +164,17 @@ internal class WriteOnlyGameCardStorage : IStorage
}
}
/// <summary>
/// An adapter that provides an <see cref="IStorageSf"/> interface for a <see cref="IStorage"/>.
/// </summary>
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
internal abstract class GameCardStorageInterfaceAdapter : IStorageSf
{
private SharedRef<IStorage> _baseStorage;
protected GameCardStorageInterfaceAdapter(ref SharedRef<IStorage> baseStorage)
protected GameCardStorageInterfaceAdapter(in SharedRef<IStorage> baseStorage)
{
_baseStorage = SharedRef<IStorage>.CreateMove(ref baseStorage);
_baseStorage = SharedRef<IStorage>.CreateCopy(in baseStorage);
}
public virtual void Dispose()

View file

@ -23,7 +23,7 @@ internal class GameCardStorageDevice : GameCardStorageInterfaceAdapter, IStorage
private readonly GameCardDummy _gc;
private GameCardStorageDevice(GameCardDummy gc, ref SharedRef<IGameCardManager> manager,
ref SharedRef<IStorage> baseStorage, GameCardHandle handle) : base(ref baseStorage)
in SharedRef<IStorage> baseStorage, GameCardHandle handle) : base(in baseStorage)
{
_manager = SharedRef<IGameCardManager>.CreateMove(ref manager);
_handle = handle;
@ -33,9 +33,9 @@ internal class GameCardStorageDevice : GameCardStorageInterfaceAdapter, IStorage
}
private GameCardStorageDevice(GameCardDummy gc, ref SharedRef<IGameCardManager> manager,
ref SharedRef<IStorage> baseStorage, GameCardHandle handle, bool isSecure, ReadOnlySpan<byte> cardDeviceId,
in SharedRef<IStorage> baseStorage, GameCardHandle handle, bool isSecure, ReadOnlySpan<byte> cardDeviceId,
ReadOnlySpan<byte> cardImageHash)
: base(ref baseStorage)
: base(in baseStorage)
{
Assert.SdkRequiresEqual(cardDeviceId.Length, Values.GcCardDeviceIdSize);
Assert.SdkRequiresEqual(cardImageHash.Length, Values.GcCardImageHashSize);
@ -51,9 +51,9 @@ internal class GameCardStorageDevice : GameCardStorageInterfaceAdapter, IStorage
}
public static SharedRef<GameCardStorageDevice> CreateShared(GameCardDummy gc,
ref SharedRef<IGameCardManager> manager, ref SharedRef<IStorage> baseStorage, GameCardHandle handle)
ref SharedRef<IGameCardManager> manager, in SharedRef<IStorage> baseStorage, GameCardHandle handle)
{
var storageDevice = new GameCardStorageDevice(gc, ref manager, ref baseStorage, handle);
var storageDevice = new GameCardStorageDevice(gc, ref manager, in baseStorage, handle);
using var sharedStorageDevice = new SharedRef<GameCardStorageDevice>(storageDevice);
storageDevice._selfReference.Set(in sharedStorageDevice);
@ -62,10 +62,10 @@ internal class GameCardStorageDevice : GameCardStorageInterfaceAdapter, IStorage
}
public static SharedRef<GameCardStorageDevice> CreateShared(GameCardDummy gc,
ref SharedRef<IGameCardManager> manager, ref SharedRef<IStorage> baseStorage, GameCardHandle handle,
ref SharedRef<IGameCardManager> manager, in SharedRef<IStorage> baseStorage, GameCardHandle handle,
bool isSecure, ReadOnlySpan<byte> cardDeviceId, ReadOnlySpan<byte> cardImageHash)
{
var storageDevice = new GameCardStorageDevice(gc, ref manager, ref baseStorage, handle, isSecure, cardDeviceId,
var storageDevice = new GameCardStorageDevice(gc, ref manager, in baseStorage, handle, isSecure, cardDeviceId,
cardImageHash);
using var sharedStorageDevice = new SharedRef<GameCardStorageDevice>(storageDevice);

View file

@ -47,6 +47,21 @@ public class MmcManager : IStorageDeviceManager
throw new NotImplementedException();
}
public Result PutToSleep()
{
throw new NotImplementedException();
}
public Result Awaken()
{
throw new NotImplementedException();
}
public Result Shutdown()
{
throw new NotImplementedException();
}
public Result Invalidate()
{
throw new NotImplementedException();

View file

@ -47,6 +47,21 @@ public class SdCardManager : IStorageDeviceManager
throw new NotImplementedException();
}
public Result PutToSleep()
{
throw new NotImplementedException();
}
public Result Awaken()
{
throw new NotImplementedException();
}
public Result Shutdown()
{
throw new NotImplementedException();
}
public Result Invalidate()
{
throw new NotImplementedException();