Begin implementing StorageDeviceManagerFactory and GameCardManager

This commit is contained in:
Alex Barney 2022-05-17 02:37:11 -07:00
parent dfd37d314f
commit 43d63086bf
24 changed files with 1730 additions and 41 deletions

View File

@ -310,8 +310,8 @@ Module,DescriptionStart,DescriptionEnd,Flags,Namespace,Name,Summary
2,2955,,,,GameCardFsCheckHandleInGetDeviceCertFailure,
2,2956,,,,GameCardFsCheckHandleInGetCardImageHashFailure,
2,2957,,,,GameCardFsCheckHandleInChallengeCardExistence,
2,2958,,,,GameCardFsCheckHandleInOnAcquireLock,
2,2959,,,,GameCardFsCheckModeInOnAcquireSecureLock,
2,2958,,,,GameCardFsCheckHandleInAcquireReadLock,
2,2959,,,,GameCardFsCheckModeInAcquireSecureLock,
2,2960,,,,GameCardFsCheckHandleInCreateReadOnlyFailure,
2,2961,,,,GameCardFsCheckHandleInCreateSecureReadOnlyFailure,
2,2962,,,,GameCardFsInvalidCompatibilityType,

1 Module DescriptionStart DescriptionEnd Flags Namespace Name Summary
310 2 3222 AllocationMemoryFailedInDataA
311 2 3223 AllocationMemoryFailedInDataB
312 2 3224 AllocationMemoryFailedInDeviceSaveDataA
313 2 3225 AllocationMemoryFailedInGameCardA
314 2 3226 AllocationMemoryFailedInGameCardB
315 2 3227 AllocationMemoryFailedInGameCardC
316 2 3228 AllocationMemoryFailedInGameCardD
317 2 3229 AllocationMemoryFailedInHostA

View File

@ -45,6 +45,12 @@ public enum GameCardSizeInternal : byte
Size32Gb = 0xE2
}
public enum GameCardClockRate
{
ClockRate25 = 25,
ClockRate50 = 50
}
[Flags]
public enum GameCardAttribute : byte
{

View File

@ -518,9 +518,9 @@ public static class ResultFs
/// <summary>Error code: 2002-2957; Inner value: 0x171a02</summary>
public static Result.Base GameCardFsCheckHandleInChallengeCardExistence => new Result.Base(ModuleFs, 2957);
/// <summary>Error code: 2002-2958; Inner value: 0x171c02</summary>
public static Result.Base GameCardFsCheckHandleInOnAcquireLock => new Result.Base(ModuleFs, 2958);
public static Result.Base GameCardFsCheckHandleInAcquireReadLock => new Result.Base(ModuleFs, 2958);
/// <summary>Error code: 2002-2959; Inner value: 0x171e02</summary>
public static Result.Base GameCardFsCheckModeInOnAcquireSecureLock => new Result.Base(ModuleFs, 2959);
public static Result.Base GameCardFsCheckModeInAcquireSecureLock => new Result.Base(ModuleFs, 2959);
/// <summary>Error code: 2002-2960; Inner value: 0x172002</summary>
public static Result.Base GameCardFsCheckHandleInCreateReadOnlyFailure => new Result.Base(ModuleFs, 2960);
/// <summary>Error code: 2002-2961; Inner value: 0x172202</summary>

View File

@ -136,8 +136,11 @@ public readonly struct BaseStorageService
Result res = GetProgramInfo(out ProgramInfo programInfo);
if (res.IsFailure()) return res.Miss();
res = _serviceImpl.OpenDeviceOperator(ref outDeviceOperator, programInfo.AccessControl);
if (res.IsFailure()) return res.Miss();
using var deviceOperator =
new SharedRef<IDeviceOperator>(new DeviceOperator(_serviceImpl.FsServer, programInfo.AccessControl,
_processId));
outDeviceOperator.SetByMove(ref deviceOperator.Ref);
return Result.Success;
}
@ -180,13 +183,11 @@ public class BaseStorageServiceImpl
{
private Configuration _config;
// LibHac addition
private SharedRef<IDeviceOperator> _deviceOperator;
internal FileSystemServer FsServer => _config.FsServer;
public BaseStorageServiceImpl(in Configuration configuration)
{
_config = configuration;
_deviceOperator = new SharedRef<IDeviceOperator>(configuration.DeviceOperator);
}
public struct Configuration
@ -196,8 +197,6 @@ public class BaseStorageServiceImpl
// LibHac additions
public FileSystemServer FsServer;
// Todo: The DeviceOperator in FS uses mostly global state. Decide how to handle this.
public IDeviceOperator DeviceOperator;
}
internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
@ -231,12 +230,4 @@ public class BaseStorageServiceImpl
return ResultFs.InvalidArgument.Log();
}
}
// LibHac addition
internal Result OpenDeviceOperator(ref SharedRef<IDeviceOperator> outDeviceOperator,
AccessControl accessControl)
{
outDeviceOperator.SetByCopy(in _deviceOperator);
return Result.Success;
}
}

View File

@ -1,7 +1,7 @@
using LibHac.Common;
using LibHac.Common.Keys;
using LibHac.FsSrv.FsCreator;
using LibHac.FsSrv.Sf;
using LibHac.FsSrv.Storage;
using LibHac.FsSystem;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
@ -10,9 +10,9 @@ namespace LibHac.FsSrv;
public class DefaultFsServerObjects
{
public FileSystemCreatorInterfaces FsCreators { get; set; }
public IDeviceOperator DeviceOperator { get; set; }
public EmulatedGameCard GameCard { get; set; }
public EmulatedSdCard SdCard { get; set; }
public EmulatedStorageDeviceManagerFactory StorageDeviceManagerFactory { get; set; }
public static DefaultFsServerObjects GetDefaultEmulatedCreators(IFileSystem rootFileSystem, KeySet keySet,
FileSystemServer fsServer, RandomDataGenerator randomGenerator)
@ -39,14 +39,14 @@ public class DefaultFsServerObjects
creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(ref sharedRootFileSystem.Ref);
creators.SdCardFileSystemCreator = new EmulatedSdCardFileSystemCreator(sdCard, ref sharedRootFileSystemCopy.Ref);
var deviceOperator = new EmulatedDeviceOperator(gameCard, sdCard);
var storageDeviceManagerFactory = new EmulatedStorageDeviceManagerFactory(fsServer, true);
return new DefaultFsServerObjects
{
FsCreators = creators,
DeviceOperator = deviceOperator,
GameCard = gameCard,
SdCard = sdCard
SdCard = sdCard,
StorageDeviceManagerFactory = storageDeviceManagerFactory
};
}
}

View File

@ -31,11 +31,11 @@ public static class FileSystemServerInitializer
if (config.FsCreators == null)
throw new ArgumentException("FsCreators must not be null");
if (config.DeviceOperator == null)
throw new ArgumentException("DeviceOperator must not be null");
if (config.StorageDeviceManagerFactory == null)
throw new ArgumentException("StorageDeviceManagerFactory must not be null");
server.SetDebugFlagEnabled(false);
server.Storage.InitializeStorageDeviceManagerFactory(null);
server.Storage.InitializeStorageDeviceManagerFactory(config.StorageDeviceManagerFactory);
FileSystemProxyConfiguration fspConfig = InitializeFileSystemProxy(server, config);
@ -55,6 +55,9 @@ public static class FileSystemServerInitializer
saveService.FixSaveData().IgnoreResult();
saveService.RecoverMultiCommit().IgnoreResult();
config.StorageDeviceManagerFactory.SetReady(StorageDevicePortId.SdCard, null);
config.StorageDeviceManagerFactory.SetReady(StorageDevicePortId.GameCard, null);
// NS usually takes care of this
if (client.Fs.IsSdCardInserted())
client.Fs.SetSdCardAccessibility(true);
@ -86,7 +89,6 @@ public static class FileSystemServerInitializer
baseStorageConfig.BisStorageCreator = config.FsCreators.BuiltInStorageCreator;
baseStorageConfig.GameCardStorageCreator = config.FsCreators.GameCardStorageCreator;
baseStorageConfig.FsServer = server;
baseStorageConfig.DeviceOperator = config.DeviceOperator;
var baseStorageService = new BaseStorageServiceImpl(in baseStorageConfig);
var timeService = new TimeServiceImpl(server);
@ -263,9 +265,9 @@ public class FileSystemServerConfig
public FileSystemCreatorInterfaces FsCreators { get; set; }
/// <summary>
/// An <see cref="IDeviceOperator"/> for managing the gamecard and SD card.
/// An <see cref="IStorageDeviceManagerFactory"/> for managing the gamecard and SD card.
/// </summary>
public IDeviceOperator DeviceOperator { get; set; }
public IStorageDeviceManagerFactory StorageDeviceManagerFactory { get; set; }
/// <summary>
/// A keyset containing rights IDs and title keys.

View File

@ -0,0 +1,150 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSrv.Storage.Sf;
using LibHac.GcSrv;
using LibHac.Os;
using LibHac.SdmmcSrv;
using LibHac.Sf;
namespace LibHac.FsSrv.Storage;
public class EmulatedStorageDeviceManagerFactory : IStorageDeviceManagerFactory
{
private SdkMutexType _gameCardDeviceMutex;
private SdkMutexType _sdCardDeviceMutex;
private SdkMutexType _mmcDeviceMutex;
private SharedRef<SdCardManager> _sdCardDeviceManager;
private SharedRef<DummyGameCardManager> _dummyGameCardDeviceManager;
private SharedRef<GameCardManager> _gameCardDeviceManager;
private SharedRef<MmcManager> _mmcDeviceManager;
private readonly bool _hasGameCard;
private readonly FileSystemServer _fsServer;
public EmulatedStorageDeviceManagerFactory(FileSystemServer fsServer, bool hasGameCard)
{
_fsServer = fsServer;
_hasGameCard = hasGameCard;
_gameCardDeviceMutex = new SdkMutexType();
_sdCardDeviceMutex = new SdkMutexType();
}
public void Dispose()
{
_sdCardDeviceManager.Destroy();
_dummyGameCardDeviceManager.Destroy();
_gameCardDeviceManager.Destroy();
_mmcDeviceManager.Destroy();
}
public Result Create(ref SharedRef<IStorageDeviceManager> outDeviceManager, StorageDevicePortId portId)
{
switch (portId)
{
case StorageDevicePortId.Mmc:
EnsureMmcReady();
outDeviceManager.SetByCopy(in _mmcDeviceManager);
break;
case StorageDevicePortId.SdCard:
{
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _sdCardDeviceMutex);
if (!_sdCardDeviceManager.HasValue)
return ResultFs.StorageDeviceNotReady.Log();
outDeviceManager.SetByCopy(in _sdCardDeviceManager);
break;
}
case StorageDevicePortId.GameCard:
{
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _gameCardDeviceMutex);
if (!_dummyGameCardDeviceManager.HasValue && !_gameCardDeviceManager.HasValue)
return ResultFs.StorageDeviceNotReady.Log();
if (_hasGameCard)
{
outDeviceManager.SetByCopy(in _gameCardDeviceManager);
}
else
{
outDeviceManager.SetByCopy(in _dummyGameCardDeviceManager);
}
break;
}
default:
return ResultFs.StorageDeviceInvalidOperation.Log();
}
return Result.Success;
}
public Result SetReady(StorageDevicePortId portId, NativeHandle handle)
{
switch (portId)
{
case StorageDevicePortId.Mmc:
EnsureMmcReady();
break;
case StorageDevicePortId.SdCard:
EnsureSdCardReady();
break;
case StorageDevicePortId.GameCard:
EnsureGameCardReady();
break;
default:
return ResultFs.StorageDeviceInvalidOperation.Log();
}
return Result.Success;
}
public Result UnsetReady(StorageDevicePortId portId)
{
return ResultFs.StorageDeviceInvalidOperation.Log();
}
private void EnsureMmcReady()
{
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mmcDeviceMutex);
if (!_mmcDeviceManager.HasValue)
{
_mmcDeviceManager.Reset(new MmcManager());
}
}
private void EnsureSdCardReady()
{
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _sdCardDeviceMutex);
if (!_sdCardDeviceManager.HasValue)
{
_sdCardDeviceManager.Reset(new SdCardManager());
// Todo: BuiltInStorageFileSystemCreator::SetSdCardPortReady
}
}
private void EnsureGameCardReady()
{
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _gameCardDeviceMutex);
if (!_dummyGameCardDeviceManager.HasValue && !_gameCardDeviceManager.HasValue)
{
if (_hasGameCard)
{
using SharedRef<GameCardManager> manger = GameCardManager.CreateShared(_fsServer);
_gameCardDeviceManager.SetByMove(ref manger.Ref);
}
else
{
_dummyGameCardDeviceManager.Reset(new DummyGameCardManager());
}
}
}
}

View File

@ -8,8 +8,8 @@ namespace LibHac.FsSrv.Storage.Sf;
public interface IStorageDeviceManager : IDisposable
{
Result IsInserted(out bool isInserted);
Result IsHandleValid(out bool isValid, uint handle);
Result OpenDetectionEvent(ref SharedRef<IEventNotifier> outEventNotifier);
Result IsHandleValid(out bool isValid, GameCardHandle handle);
Result OpenDetectionEvent(ref SharedRef<IEventNotifier> outDetectionEvent);
Result OpenOperator(ref SharedRef<IStorageDeviceOperator> outDeviceOperator);
Result OpenDevice(ref SharedRef<IStorageDevice> outStorageDevice, ulong attribute);
Result OpenStorage(ref SharedRef<IStorageSf> outStorage, ulong attribute);

View File

@ -33,7 +33,7 @@ internal static class StorageDeviceManagerFactory
IStorageDeviceManagerFactory factory = storage.GetStorageDeviceManagerFactory(null);
Assert.SdkNotNull(factory);
return factory.Create(ref outDeviceManager, portId);
return factory.Create(ref outDeviceManager, portId).Ret();
}
public static IStorageDeviceManagerFactory GetStorageDeviceManagerFactory(this StorageService storage,

View File

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using LibHac.Common;
using LibHac.FsSrv.Sf;
using LibHac.Os;
using LibHac.Sf;
namespace LibHac.FsSystem;
internal class CardDeviceDetectionEventManager : IDisposable
{
private LinkedList<CardDeviceDetectionEvent> _events;
private SdkMutex _mutex;
protected CallbackArguments CallbackArgs;
protected class CallbackArguments
{
public CardDeviceDetectionEventManager EventManager;
public SdkMutex Mutex;
public Sdmmc.Port Port;
}
public CardDeviceDetectionEventManager()
{
_events = new LinkedList<CardDeviceDetectionEvent>();
_mutex = new SdkMutex();
CallbackArgs = new CallbackArguments { EventManager = this, Mutex = _mutex };
}
public virtual void Dispose()
{
throw new NotImplementedException();
}
public Result CreateDetectionEvent(ref SharedRef<IEventNotifier> outDetectionEvent)
{
throw new NotImplementedException();
}
public void Unlink(CardDeviceDetectionEvent detectionEvent)
{
throw new NotImplementedException();
}
public void SignalAll()
{
throw new NotImplementedException();
}
protected static void DetectionEventCallback(object args)
{
throw new NotImplementedException();
}
}
internal class CardDeviceDetectionEvent : IEventNotifier
{
private CardDeviceDetectionEventManager _eventManager;
// Todo: SystemEvent
public CardDeviceDetectionEvent(CardDeviceDetectionEventManager eventManager)
{
_eventManager = eventManager;
}
public void Dispose()
{
throw new NotImplementedException();
}
public Result GetEventHandle(out NativeHandle handle)
{
throw new NotImplementedException();
}
}

View File

@ -0,0 +1,200 @@
using System;
using LibHac.Fs;
using LibHac.Gc.Impl;
using LibHac.Gc.Writer;
namespace LibHac.Gc;
public class GameCardDummy
{
public GameCardWriter Writer => new GameCardWriter();
public readonly struct GameCardWriter
{
public GameCardWriter()
{
}
public void ChangeMode(AsicMode mode)
{
throw new NotImplementedException();
}
public Result ActivateForWriter()
{
throw new NotImplementedException();
}
public Result EraseAndWriteParameter(MemorySize size, uint romAreaStartPageIndex)
{
throw new NotImplementedException();
}
public Result Write(ReadOnlySpan<byte> source, uint pageIndex, uint pageCount)
{
throw new NotImplementedException();
}
public Result GetCardAvailableRawSize(out long outSize)
{
throw new NotImplementedException();
}
public void SetVerifyEnableFlag(bool isEnabled)
{
throw new NotImplementedException();
}
public void SetUserAsicFirmwareBuffer(ReadOnlySpan<byte> firmwareBuffer)
{
throw new NotImplementedException();
}
public Result GetRmaInformation(out RmaInformation outRmaInformation)
{
throw new NotImplementedException();
}
public Result WriteDevCardParam(in DevCardParameter devCardParam)
{
throw new NotImplementedException();
}
public Result ReadDevCardParam(out DevCardParameter outDevCardParam)
{
throw new NotImplementedException();
}
public Result ForceErase()
{
throw new NotImplementedException();
}
}
public void PresetInternalKeys(ReadOnlySpan<byte> gameCardKey, ReadOnlySpan<byte> gameCardCertificate)
{
throw new NotImplementedException();
}
public void Initialize(Memory<byte> workBuffer, ulong deviceBufferAddress)
{
throw new NotImplementedException();
}
public void FinalizeGc()
{
throw new NotImplementedException();
}
public void PowerOffGameCard()
{
throw new NotImplementedException();
}
public void RegisterDeviceVirtualAddress(Memory<byte> buffer, ulong deviceBufferAddress)
{
throw new NotImplementedException();
}
public void UnregisterDeviceVirtualAddress(Memory<byte> buffer, ulong deviceBufferAddress)
{
throw new NotImplementedException();
}
public Result GetInitializationResult()
{
throw new NotImplementedException();
}
public Result Activate()
{
throw new NotImplementedException();
}
public void Deactivate()
{
throw new NotImplementedException();
}
public Result SetCardToSecureMode()
{
throw new NotImplementedException();
}
public Result Read(Span<byte> destination, uint pageIndex, uint pageCount)
{
throw new NotImplementedException();
}
public void PutToSleep()
{
throw new NotImplementedException();
}
public void Awaken()
{
throw new NotImplementedException();
}
public bool IsCardInserted()
{
throw new NotImplementedException();
}
public bool IsCardActivationValid()
{
throw new NotImplementedException();
}
public Result GetCardStatus(out GameCardStatus outStatus)
{
throw new NotImplementedException();
}
public Result GetCardDeviceId(Span<byte> destBuffer)
{
throw new NotImplementedException();
}
public Result GetCardDeviceCertificate(Span<byte> destBuffer)
{
throw new NotImplementedException();
}
public Result ChallengeCardExistence(Span<byte> responseBuffer, ReadOnlySpan<byte> challengeSeedBuffer,
ReadOnlySpan<byte> challengeValueBuffer)
{
throw new NotImplementedException();
}
public Result GetCardImageHash(Span<byte> destBuffer)
{
throw new NotImplementedException();
}
public Result GetGameCardIdSet(out GameCardIdSet outGcIdSet)
{
throw new NotImplementedException();
}
public void RegisterDetectionEventCallback(Action<object> function, object args)
{
throw new NotImplementedException();
}
public void UnregisterDetectionEventCallback()
{
throw new NotImplementedException();
}
public Result GetCardHeader(Span<byte> destBuffer)
{
throw new NotImplementedException();
}
public Result GetErrorInfo(out GameCardErrorReportInfo outErrorReportInfo)
{
throw new NotImplementedException();
}
}

View File

@ -22,6 +22,11 @@ public struct CardId3
public Array4<byte> Reserved;
}
public struct DevCardParameter
{
public Array512<byte> Data;
}
public struct CardInitialDataPayload
{
public Array8<byte> PackageId;

View File

@ -0,0 +1,19 @@
namespace LibHac.Gc.Writer;
public enum AsicMode : byte
{
Read = 0,
Write = 1
}
public enum MemorySize
{
// ReSharper disable InconsistentNaming
Size1GB = 1,
Size2GB = 2,
Size4GB = 4,
Size8GB = 8,
Size16GB = 16,
Size32GB = 32
// ReSharper restore InconsistentNaming
}

View File

@ -0,0 +1,189 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSrv.Sf;
using LibHac.FsSrv.Storage.Sf;
using LibHac.Sf;
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
namespace LibHac.GcSrv;
public class DummyGameCardManager : IStorageDeviceManager, IStorageDeviceOperator
{
public DummyGameCardManager()
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
public Result IsInserted(out bool isInserted)
{
isInserted = false;
return Result.Success;
}
public Result IsHandleValid(out bool isValid, uint handle)
{
isValid = false;
return Result.Success;
}
public Result OpenDetectionEvent(ref SharedRef<IEventNotifier> outDetectionEvent)
{
throw new NotImplementedException();
}
public Result OpenOperator(ref SharedRef<IStorageDeviceOperator> outDeviceOperator)
{
throw new NotImplementedException();
}
public Result OpenDevice(ref SharedRef<IStorageDevice> outStorageDevice, ulong attribute)
{
return ResultFs.GameCardCardNotInserted.Log();
}
public Result OpenStorage(ref SharedRef<IStorageSf> outStorage, ulong attribute)
{
return ResultFs.GameCardCardNotInserted.Log();
}
public Result Invalidate()
{
return Result.Success;
}
public Result Operate(int operationId)
{
var operation = (GameCardManagerOperationIdValue)operationId;
switch (operation)
{
case GameCardManagerOperationIdValue.Finalize:
return Result.Success;
case GameCardManagerOperationIdValue.GetInitializationResult:
return Result.Success;
case GameCardManagerOperationIdValue.ForceErase:
return ResultFs.GameCardCardNotInserted.Log();
case GameCardManagerOperationIdValue.SimulateDetectionEventSignaled:
return ResultFs.GameCardNotSupportedOnDeviceModel.Log();
default:
return ResultFs.InvalidArgument.Log();
}
}
public Result OperateIn(InBuffer buffer, long offset, long size, int operationId)
{
var operation = (GameCardManagerOperationIdValue)operationId;
switch (operation)
{
case GameCardManagerOperationIdValue.SetVerifyEnableFlag:
return Result.Success;
case GameCardManagerOperationIdValue.EraseAndWriteParamDirectly:
return ResultFs.GameCardCardNotInserted.Log();
default:
return ResultFs.InvalidArgument.Log();
}
}
public Result OperateOut(out long bytesWritten, OutBuffer buffer, int operationId)
{
var operation = (GameCardManagerOperationIdValue)operationId;
bytesWritten = 0;
switch (operation)
{
case GameCardManagerOperationIdValue.GetHandle:
{
return ResultFs.GameCardCardNotInserted.Log();
}
case GameCardManagerOperationIdValue.GetGameCardErrorInfo:
{
if (buffer.Size < Unsafe.SizeOf<GameCardErrorInfo>())
return ResultFs.InvalidArgument.Log();
buffer.As<GameCardErrorInfo>() = default;
bytesWritten = Unsafe.SizeOf<GameCardErrorInfo>();
return Result.Success;
}
case GameCardManagerOperationIdValue.GetGameCardErrorReportInfo:
{
if (buffer.Size < Unsafe.SizeOf<GameCardErrorReportInfo>())
return ResultFs.InvalidArgument.Log();
buffer.As<GameCardErrorReportInfo>() = default;
bytesWritten = Unsafe.SizeOf<GameCardErrorReportInfo>();
return Result.Success;
}
case GameCardManagerOperationIdValue.ReadParamDirectly:
{
return ResultFs.GameCardCardNotInserted.Log();
}
default:
return ResultFs.InvalidArgument.Log();
}
}
public Result OperateOut2(out long bytesWrittenBuffer1, OutBuffer buffer1, out long bytesWrittenBuffer2,
OutBuffer buffer2, int operationId)
{
bytesWrittenBuffer1 = 0;
bytesWrittenBuffer2 = 0;
return ResultFs.NotImplemented.Log();
}
public Result OperateInOut(out long bytesWritten, OutBuffer outBuffer, InBuffer inBuffer, long offset, long size,
int operationId)
{
var operation = (GameCardManagerOperationIdValue)operationId;
bytesWritten = 0;
switch (operation)
{
case GameCardManagerOperationIdValue.IsGameCardActivationValid:
{
if (outBuffer.Size < Unsafe.SizeOf<bool>())
return ResultFs.InvalidArgument.Log();
outBuffer.As<bool>() = false;
bytesWritten = sizeof(bool);
return Result.Success;
}
case GameCardManagerOperationIdValue.GetGameCardAsicInfo:
return ResultFs.GameCardAccessFailed.Log();
case GameCardManagerOperationIdValue.GetGameCardDeviceIdForProdCard:
return ResultFs.GameCardCardNotInserted.Log();
case GameCardManagerOperationIdValue.WriteToGameCardDirectly:
return ResultFs.GameCardCardNotInserted.Log();
default:
return ResultFs.InvalidArgument.Log();
}
}
public Result OperateIn2Out(out long bytesWritten, OutBuffer outBuffer, InBuffer inBuffer1, InBuffer inBuffer2,
long offset, long size, int operationId)
{
bytesWritten = 0;
return ResultFs.NotImplemented.Log();
}
}

View File

@ -0,0 +1,17 @@
using System;
using LibHac.FsSystem;
namespace LibHac.GcSrv;
internal class GameCardDeviceDetectionEventManager : CardDeviceDetectionEventManager
{
public GameCardDeviceDetectionEventManager()
{
throw new NotImplementedException();
}
public override void Dispose()
{
throw new NotImplementedException();
}
}

View File

@ -0,0 +1,897 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
using LibHac.FsSrv.Storage.Sf;
using LibHac.FsSystem;
using LibHac.Gc;
using LibHac.Gc.Impl;
using LibHac.Gc.Writer;
using LibHac.Os;
using LibHac.Sf;
using IStorage = LibHac.FsSrv.Sf.IStorage;
namespace LibHac.GcSrv;
public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IGameCardDeviceManager
{
private enum CardState
{
Initial = 0,
Normal = 1,
Secure = 2,
Write = 3
}
private ReaderWriterLock _rwLock;
private bool _isInitialized;
private bool _isFinalized;
private CardState _state;
private GameCardHandle _currentHandle;
private GameCardDeviceDetectionEventManager _detectionEventManager;
// LibHac additions
private WeakRef<GameCardManager> _selfReference;
private readonly FileSystemServer _fsServer;
private readonly GameCardDummy _gc;
private GameCardManager(FileSystemServer fsServer)
{
_rwLock = new ReaderWriterLock(fsServer.Hos.Os);
_fsServer = fsServer;
}
public static SharedRef<GameCardManager> CreateShared(FileSystemServer fsServer)
{
var manager = new GameCardManager(fsServer);
using var sharedManager = new SharedRef<GameCardManager>(manager);
manager._selfReference.Set(in sharedManager);
return SharedRef<GameCardManager>.CreateMove(ref sharedManager.Ref);
}
public void Dispose()
{
_detectionEventManager?.Dispose();
_detectionEventManager = null;
_rwLock?.Dispose();
_rwLock = null;
}
private uint BytesToPages(long byteCount)
{
return (uint)((ulong)byteCount / (ulong)Values.GcPageSize);
}
private void DeactivateAndChangeState()
{
_gc.Deactivate();
_currentHandle++;
_state = CardState.Initial;
}
private void CheckGameCardAndDeactivate()
{
if (_state != CardState.Initial && !_gc.IsCardActivationValid())
{
DeactivateAndChangeState();
}
}
private Result ActivateGameCard()
{
Result res = HandleGameCardAccessResult(_gc.Activate());
if (res.IsFailure()) return res.Miss();
return Result.Success;
}
private Result ActivateGameCardForWriter()
{
return HandleGameCardAccessResult(_gc.Writer.ActivateForWriter());
}
private Result SetGameCardToSecureMode()
{
return HandleGameCardAccessResult(_gc.SetCardToSecureMode());
}
public Result IsInserted(out bool isInserted)
{
UnsafeHelpers.SkipParamInit(out isInserted);
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
isInserted = _gc.IsCardInserted();
return Result.Success;
}
public Result InitializeGcLibrary()
{
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
if (_isFinalized)
return ResultFs.PreconditionViolation.Log();
if (_isInitialized)
return Result.Success;
// Missing: Wait on settings-ready event
// Missing: Allocate work buffer and pass it to nn::gc::Initialize
_gc.Initialize(default, default);
// Missing: Register the device buffer
_detectionEventManager = new GameCardDeviceDetectionEventManager();
_isInitialized = true;
return Result.Success;
}
private Result EnsureGameCardNormalMode(out GameCardHandle outNewHandle)
{
UnsafeHelpers.SkipParamInit(out outNewHandle);
if (_state == CardState.Normal)
CheckGameCardAndDeactivate();
switch (_state)
{
case CardState.Initial:
{
// Initial -> Normal
Result res = ActivateGameCard();
if (res.IsFailure()) return res.Miss();
_state = CardState.Normal;
break;
}
case CardState.Normal:
{
outNewHandle = _currentHandle;
return Result.Success;
}
case CardState.Secure:
{
// Secure -> Initial -> Normal
DeactivateAndChangeState();
Result res = ActivateGameCard();
if (res.IsFailure()) return res.Miss();
_state = CardState.Normal;
break;
}
case CardState.Write:
{
// Write -> Initial -> Normal
DeactivateAndChangeState();
_gc.Writer.ChangeMode(AsicMode.Read);
Result res = ActivateGameCard();
if (res.IsFailure()) return res.Miss();
_state = CardState.Normal;
break;
}
default:
Abort.UnexpectedDefault();
break;
}
outNewHandle = _currentHandle;
return Result.Success;
}
private Result EnsureGameCardSecureMode(out GameCardHandle outNewHandle)
{
UnsafeHelpers.SkipParamInit(out outNewHandle);
if (_state == CardState.Secure)
CheckGameCardAndDeactivate();
switch (_state)
{
case CardState.Initial:
{
// Initial -> Normal -> Secure
Result res = ActivateGameCard();
if (res.IsFailure()) return res.Miss();
_state = CardState.Normal;
res = SetGameCardToSecureMode();
if (res.IsFailure()) return res.Miss();
_state = CardState.Secure;
break;
}
case CardState.Normal:
{
// Normal -> Secure
Result res = SetGameCardToSecureMode();
if (res.IsFailure()) return res.Miss();
_state = CardState.Secure;
break;
}
case CardState.Secure:
{
outNewHandle = _currentHandle;
return Result.Success;
}
case CardState.Write:
{
// Write -> Initial -> Normal -> Secure
DeactivateAndChangeState();
_gc.Writer.ChangeMode(AsicMode.Read);
Result res = ActivateGameCard();
if (res.IsFailure()) return res.Miss();
_state = CardState.Normal;
res = SetGameCardToSecureMode();
if (res.IsFailure()) return res.Miss();
_state = CardState.Secure;
break;
}
default:
Abort.UnexpectedDefault();
break;
}
outNewHandle = _currentHandle;
return Result.Success;
}
private Result EnsureGameCardWriteMode(out GameCardHandle outNewHandle)
{
UnsafeHelpers.SkipParamInit(out outNewHandle);
switch (_state)
{
case CardState.Initial:
{
// Initial -> Write
_gc.Writer.ChangeMode(AsicMode.Write);
Result res = ActivateGameCardForWriter();
if (res.IsFailure()) return res.Miss();
_state = CardState.Write;
break;
}
case CardState.Normal:
case CardState.Secure:
{
// Normal/Secure -> Initial -> Write
DeactivateAndChangeState();
_gc.Writer.ChangeMode(AsicMode.Write);
Result res = ActivateGameCardForWriter();
if (res.IsFailure()) return res.Miss();
_state = CardState.Write;
break;
}
case CardState.Write:
{
outNewHandle = _currentHandle;
return Result.Success;
}
default:
Abort.UnexpectedDefault();
break;
}
outNewHandle = _currentHandle;
return Result.Success;
}
public Result IsHandleValid(out bool isValid, GameCardHandle handle)
{
UnsafeHelpers.SkipParamInit(out isValid);
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
using var readLock = new SharedLock<ReaderWriterLock>();
isValid = AcquireReadLock(ref readLock.Ref(), handle).IsSuccess();
return Result.Success;
}
public Result OpenDetectionEvent(ref SharedRef<IEventNotifier> outDetectionEvent)
{
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
res = _detectionEventManager.CreateDetectionEvent(ref outDetectionEvent);
if (res.IsFailure()) return res.Miss();
return Result.Success;
}
public Result OpenOperator(ref SharedRef<IStorageDeviceOperator> outDeviceOperator)
{
throw new NotImplementedException();
}
public Result OpenDevice(ref SharedRef<IStorageDevice> outStorageDevice, ulong attribute)
{
throw new NotImplementedException();
}
public Result OpenStorage(ref SharedRef<IStorage> outStorage, ulong attribute)
{
throw new NotImplementedException();
}
public Result PutToSleep()
{
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
_gc.PutToSleep();
return Result.Success;
}
public Result Awaken()
{
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
_gc.Awaken();
return Result.Success;
}
public Result Shutdown()
{
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
_gc.PutToSleep();
return Result.Success;
}
public Result Invalidate()
{
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
DeactivateAndChangeState();
return Result.Success;
}
public Result Operate(int operationId)
{
var operation = (GameCardManagerOperationIdValue)operationId;
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
switch (operation)
{
case GameCardManagerOperationIdValue.Finalize:
FinalizeGcLibrary();
return Result.Success;
case GameCardManagerOperationIdValue.GetInitializationResult:
return GetInitializationResult().Ret();
case GameCardManagerOperationIdValue.ForceErase:
return ForceEraseGameCard().Ret();
case GameCardManagerOperationIdValue.SimulateDetectionEventSignaled:
_detectionEventManager.SignalAll();
return Result.Success;
default:
return ResultFs.InvalidArgument.Log();
}
}
public Result OperateIn(InBuffer buffer, long offset, long size, int operationId)
{
var operation = (GameCardManagerOperationIdValue)operationId;
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
switch (operation)
{
case GameCardManagerOperationIdValue.SetVerifyEnableFlag:
if (buffer.Size < sizeof(bool))
return ResultFs.InvalidArgument.Log();
SetVerifyEnableFlag(buffer.As<bool>());
return Result.Success;
case GameCardManagerOperationIdValue.EraseAndWriteParamDirectly:
if (buffer.Size < Unsafe.SizeOf<DevCardParameter>())
return ResultFs.InvalidArgument.Log();
res = EraseAndWriteParamDirectly(buffer.Buffer);
if (res.IsFailure()) return res.Miss();
return Result.Success;
default:
return ResultFs.InvalidArgument.Log();
}
}
public Result OperateOut(out long bytesWritten, OutBuffer buffer, int operationId)
{
var operation = (GameCardManagerOperationIdValue)operationId;
bytesWritten = 0;
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
switch (operation)
{
case GameCardManagerOperationIdValue.GetHandle:
{
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
if (buffer.Size < sizeof(GameCardHandle))
return ResultFs.InvalidArgument.Log();
res = GetHandle(out buffer.As<GameCardHandle>());
if (res.IsFailure()) return res.Miss();
bytesWritten = sizeof(GameCardHandle);
return Result.Success;
}
case GameCardManagerOperationIdValue.GetGameCardErrorInfo:
if (buffer.Size < Unsafe.SizeOf<GameCardErrorInfo>())
return ResultFs.InvalidArgument.Log();
res = GetGameCardErrorInfo(out buffer.As<GameCardErrorInfo>());
if (res.IsFailure()) return res.Miss();
bytesWritten = Unsafe.SizeOf<GameCardErrorInfo>();
return Result.Success;
case GameCardManagerOperationIdValue.GetGameCardErrorReportInfo:
if (buffer.Size < Unsafe.SizeOf<GameCardErrorReportInfo>())
return ResultFs.InvalidArgument.Log();
res = GetGameCardErrorReportInfo(out buffer.As<GameCardErrorReportInfo>());
if (res.IsFailure()) return res.Miss();
bytesWritten = Unsafe.SizeOf<GameCardErrorReportInfo>();
return Result.Success;
case GameCardManagerOperationIdValue.ReadParamDirectly:
if (buffer.Size < Unsafe.SizeOf<DevCardParameter>())
return ResultFs.InvalidArgument.Log();
res = ReadParamDirectly(buffer.Buffer);
if (res.IsFailure()) return res.Miss();
bytesWritten = Unsafe.SizeOf<DevCardParameter>();
return Result.Success;
default:
return ResultFs.InvalidArgument.Log();
}
}
public Result OperateOut2(out long bytesWrittenBuffer1, OutBuffer buffer1, out long bytesWrittenBuffer2,
OutBuffer buffer2, int operationId)
{
UnsafeHelpers.SkipParamInit(out bytesWrittenBuffer1, out bytesWrittenBuffer2);
return ResultFs.NotImplemented.Log();
}
public Result OperateInOut(out long bytesWritten, OutBuffer outBuffer, InBuffer inBuffer, long offset, long size,
int operationId)
{
var operation = (GameCardManagerOperationIdValue)operationId;
bytesWritten = 0;
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
switch (operation)
{
case GameCardManagerOperationIdValue.IsGameCardActivationValid:
if (inBuffer.Size != sizeof(GameCardHandle))
return ResultFs.InvalidArgument.Log();
if (outBuffer.Size < sizeof(bool))
return ResultFs.InvalidArgument.Log();
outBuffer.As<bool>() = IsGameCardActivationValid(inBuffer.As<GameCardHandle>());
bytesWritten = sizeof(bool);
return Result.Success;
case GameCardManagerOperationIdValue.GetGameCardAsicInfo:
if (inBuffer.Size != Values.GcAsicFirmwareSize)
return ResultFs.InvalidArgument.Log();
if (outBuffer.Size < Unsafe.SizeOf<RmaInformation>())
return ResultFs.InvalidArgument.Log();
res = GetGameCardAsicInfo(out RmaInformation rmaInfo, inBuffer.Buffer);
if (res.IsFailure()) return res.Miss();
SpanHelpers.AsReadOnlyByteSpan(in rmaInfo).CopyTo(outBuffer.Buffer);
bytesWritten = Unsafe.SizeOf<RmaInformation>();
return Result.Success;
case GameCardManagerOperationIdValue.GetGameCardDeviceIdForProdCard:
if (inBuffer.Size < Values.GcPageSize)
return ResultFs.InvalidArgument.Log();
if (outBuffer.Size < Values.GcPageSize)
return ResultFs.InvalidArgument.Log();
res = GetGameCardDeviceIdForProdCard(outBuffer.Buffer, inBuffer.Buffer);
if (res.IsFailure()) return res.Miss();
bytesWritten = Values.GcPageSize;
return Result.Success;
case GameCardManagerOperationIdValue.WriteToGameCardDirectly:
return WriteToGameCardDirectly(offset, outBuffer.Buffer.Slice(0, (int)size)).Ret();
default:
return ResultFs.InvalidArgument.Log();
}
}
public Result OperateIn2Out(out long bytesWritten, OutBuffer outBuffer, InBuffer inBuffer1, InBuffer inBuffer2,
long offset, long size, int operationId)
{
UnsafeHelpers.SkipParamInit(out bytesWritten);
return ResultFs.NotImplemented.Log();
}
private void FinalizeGcLibrary()
{
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
if (_isInitialized)
{
_gc.UnregisterDetectionEventCallback();
_isFinalized = true;
_gc.FinalizeGc();
// nn::gc::UnregisterDeviceVirtualAddress
}
}
private bool IsGameCardActivationValid(GameCardHandle handle)
{
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
return handle == _currentHandle && _gc.IsCardActivationValid();
}
private Result GetInitializationResult()
{
return _gc.GetInitializationResult();
}
private Result GetGameCardErrorInfo(out GameCardErrorInfo outErrorInfo)
{
outErrorInfo = default;
Result res = _gc.GetErrorInfo(out GameCardErrorReportInfo errorInfo);
if (res.IsFailure()) return res.Miss();
outErrorInfo.GameCardCrcErrorCount = errorInfo.ErrorInfo.GameCardCrcErrorCount;
outErrorInfo.AsicCrcErrorCount = errorInfo.ErrorInfo.AsicCrcErrorCount;
outErrorInfo.RefreshCount = errorInfo.ErrorInfo.RefreshCount;
outErrorInfo.TimeoutRetryErrorCount = errorInfo.ErrorInfo.TimeoutRetryErrorCount;
outErrorInfo.ReadRetryCount = errorInfo.ErrorInfo.ReadRetryCount;
return Result.Success;
}
private Result GetGameCardErrorReportInfo(out GameCardErrorReportInfo outErrorInfo)
{
Result res = _gc.GetErrorInfo(out outErrorInfo);
if (res.IsFailure()) return res.Miss();
return Result.Success;
}
private void SetVerifyEnableFlag(bool isEnabled)
{
_gc.Writer.SetVerifyEnableFlag(isEnabled);
}
private Result GetGameCardAsicInfo(out RmaInformation outRmaInfo, ReadOnlySpan<byte> asicFirmwareBuffer)
{
UnsafeHelpers.SkipParamInit(out outRmaInfo);
Assert.SdkRequiresEqual(asicFirmwareBuffer.Length, Values.GcAsicFirmwareSize);
_gc.Writer.SetUserAsicFirmwareBuffer(asicFirmwareBuffer);
_gc.Writer.ChangeMode(AsicMode.Write);
Result res = _gc.Writer.GetRmaInformation(out RmaInformation rmaInfo);
if (res.IsFailure()) return res.Miss();
outRmaInfo = rmaInfo;
return Result.Success;
}
private Result GetGameCardDeviceIdForProdCard(Span<byte> outBuffer, ReadOnlySpan<byte> devHeaderBuffer)
{
Assert.SdkRequiresGreaterEqual(outBuffer.Length, Values.GcPageSize);
Assert.SdkRequiresGreaterEqual(devHeaderBuffer.Length, Values.GcPageSize);
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
int writeSize = Values.GcPageSize;
var pooledBuffer = new PooledBuffer(writeSize, writeSize);
Assert.SdkGreaterEqual(pooledBuffer.GetSize(), writeSize);
// Read the current card header into a temporary buffer
_gc.Writer.ChangeMode(AsicMode.Read);
Span<byte> tmpBuffer = stackalloc byte[writeSize];
tmpBuffer.Clear();
_gc.GetCardHeader(pooledBuffer.GetBuffer());
if (res.IsFailure()) return res.Miss();
pooledBuffer.GetBuffer().CopyTo(tmpBuffer);
// Write the provided card header
_gc.Writer.ChangeMode(AsicMode.Write);
res = HandleGameCardAccessResult(_gc.Writer.ActivateForWriter());
if (res.IsFailure()) return res.Miss();
devHeaderBuffer.CopyTo(pooledBuffer.GetBuffer());
res = _gc.Writer.Write(pooledBuffer.GetBuffer(), 8, 1);
if (res.IsFailure()) return res.Miss();
// Read the cert area
_gc.Writer.ChangeMode(AsicMode.Read);
res = _gc.Activate();
if (res.IsFailure()) return res.Miss();
res = _gc.Read(pooledBuffer.GetBuffer(), 0x38, 1);
if (res.IsFailure()) return res.Miss();
Span<byte> deviceCert = stackalloc byte[writeSize];
pooledBuffer.GetBuffer().CopyTo(deviceCert);
// Restore the original card header
_gc.Writer.ChangeMode(AsicMode.Write);
res = HandleGameCardAccessResult(_gc.Writer.ActivateForWriter());
if (res.IsFailure()) return res.Miss();
tmpBuffer.CopyTo(pooledBuffer.GetBuffer());
res = _gc.Writer.Write(pooledBuffer.GetBuffer(), 8, 1);
if (res.IsFailure()) return res.Miss();
deviceCert.CopyTo(outBuffer);
return Result.Success;
}
private Result EraseAndWriteParamDirectly(ReadOnlySpan<byte> inBuffer)
{
Assert.SdkRequires(inBuffer.Length >= Unsafe.SizeOf<DevCardParameter>());
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
var devCardParam = SpanHelpers.AsReadOnlyStruct<DevCardParameter>(inBuffer);
return _gc.Writer.WriteDevCardParam(in devCardParam).Ret();
}
private Result ReadParamDirectly(Span<byte> outBuffer)
{
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
res = _gc.Writer.ReadDevCardParam(out DevCardParameter devCardParam);
if (res.IsFailure()) return res.Miss();
SpanHelpers.AsReadOnlyByteSpan(in devCardParam).CopyTo(outBuffer);
return Result.Success;
}
private Result WriteToGameCardDirectly(long offset, Span<byte> buffer)
{
Result res;
using (new SharedLock<ReaderWriterLock>(_rwLock))
{
if (buffer.Length == 0)
return Result.Success;
res = _gc.Writer.Write(buffer, BytesToPages(offset), BytesToPages(buffer.Length));
}
if (res != Result.Success)
{
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
res = HandleGameCardAccessResult(res);
}
if (res.IsFailure()) return res.Miss();
return Result.Success;
}
private Result ForceEraseGameCard()
{
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
_gc.Writer.ChangeMode(AsicMode.Write);
res = _gc.Writer.ForceErase();
if (res.IsFailure()) return res.Miss();
return Result.Success;
}
public Result AcquireReadLock(ref SharedLock<ReaderWriterLock> outLock, GameCardHandle handle)
{
using var readLock = new SharedLock<ReaderWriterLock>(_rwLock);
if (_state != CardState.Initial && !_gc.IsCardActivationValid())
{
readLock.Unlock();
Invalidate().IgnoreResult();
return ResultFs.GameCardFsCheckHandleInAcquireReadLock.Log();
}
if (_currentHandle != handle)
return ResultFs.GameCardFsCheckHandleInAcquireReadLock.Log();
outLock.Set(ref readLock.Ref());
return Result.Success;
}
public Result AcquireSecureLock(ref SharedLock<ReaderWriterLock> outLock, ref GameCardHandle handle,
ReadOnlySpan<byte> cardDeviceId, ReadOnlySpan<byte> cardImageHash)
{
using (var readLock = new SharedLock<ReaderWriterLock>(_rwLock))
{
if (!IsSecureMode())
{
return ResultFs.GameCardFsCheckModeInAcquireSecureLock.Log();
}
if (_state != CardState.Initial && !_gc.IsCardActivationValid())
{
readLock.Unlock();
Invalidate().IgnoreResult();
}
else if (_currentHandle == handle)
{
outLock.Set(ref readLock.Ref());
return Result.Success;
}
}
GameCardHandle newHandle;
using (new UniqueLock<ReaderWriterLock>(_rwLock))
{
if (!IsSecureMode())
{
return ResultFs.GameCardFsCheckModeInAcquireSecureLock.Log();
}
Span<byte> currentCardDeviceId = stackalloc byte[Values.GcCardDeviceIdSize];
Span<byte> currentCardImageHash = stackalloc byte[Values.GcCardImageHashSize];
Result res = HandleGameCardAccessResult(_gc.GetCardDeviceId(currentCardDeviceId));
if (res.IsFailure()) return res.Miss();
res = HandleGameCardAccessResult(_gc.GetCardImageHash(currentCardImageHash));
if (res.IsFailure()) return res.Miss();
if (!Crypto.CryptoUtil.IsSameBytes(currentCardDeviceId, cardDeviceId, Values.GcCardDeviceIdSize) ||
!Crypto.CryptoUtil.IsSameBytes(currentCardImageHash, cardImageHash, Values.GcCardImageHashSize))
return ResultFs.GameCardFsCheckModeInAcquireSecureLock.Log();
res = GetHandle(out newHandle);
if (res.IsFailure()) return res.Miss();
}
using (var readLock = new SharedLock<ReaderWriterLock>())
{
Result res = AcquireReadLock(ref readLock.Ref(), newHandle);
if (res.IsFailure()) return res.Miss();
handle = newHandle;
outLock.Set(ref readLock.Ref());
return Result.Success;
}
}
public Result AcquireWriteLock(ref UniqueLock<ReaderWriterLock> outLock)
{
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
outLock.Set(ref writeLock.Ref());
return Result.Success;
}
public Result HandleGameCardAccessResult(Result result)
{
if (result.IsFailure())
{
DeactivateAndChangeState();
}
return result;
}
public Result GetHandle(out GameCardHandle outHandle)
{
UnsafeHelpers.SkipParamInit(out outHandle);
if (_state == CardState.Normal || _state == CardState.Secure)
{
CheckGameCardAndDeactivate();
}
switch (_state)
{
case CardState.Initial:
{
Result res = ActivateGameCard();
if (res.IsFailure()) return res.Miss();
break;
}
case CardState.Normal:
case CardState.Secure:
break;
case CardState.Write:
{
DeactivateAndChangeState();
_gc.Writer.ChangeMode(AsicMode.Read);
Result res = ActivateGameCard();
if (res.IsFailure()) return res.Miss();
_state = CardState.Normal;
break;
}
default:
Abort.UnexpectedDefault();
break;
}
outHandle = _currentHandle;
return Result.Success;
}
public bool IsSecureMode()
{
return _state == CardState.Secure;
}
}

View File

@ -1,14 +1,14 @@
using System;
using LibHac.Fs.Impl;
using LibHac.Os;
namespace LibHac.GcSrv;
internal interface IGameCardDeviceManager
{
Result AcquireReadLock(out UniqueLock locker, uint handle);
Result AcquireReadLockSecureMode(out UniqueLock locker, ref uint handle, ReadOnlySpan<byte> cardDeviceId, ReadOnlySpan<byte> cardImageHash);
Result AcquireWriteLock(out SharedLock locker);
Result AcquireReadLock(ref SharedLock<ReaderWriterLock> outLock, GameCardHandle handle);
Result AcquireSecureLock(ref SharedLock<ReaderWriterLock> outLock, ref GameCardHandle handle, ReadOnlySpan<byte> cardDeviceId, ReadOnlySpan<byte> cardImageHash);
Result AcquireWriteLock(ref UniqueLock<ReaderWriterLock> outLock);
Result HandleGameCardAccessResult(Result result);
Result GetHandle(out uint handle);
Result GetHandle(out GameCardHandle outHandle);
bool IsSecureMode();
}

View File

@ -24,9 +24,9 @@ public static class HorizonFactory
var fsServerConfig = new FileSystemServerConfig
{
DeviceOperator = defaultObjects.DeviceOperator,
ExternalKeySet = keySet.ExternalKeySet,
FsCreators = defaultObjects.FsCreators,
StorageDeviceManagerFactory = defaultObjects.StorageDeviceManagerFactory,
RandomGenerator = randomGenerator
};

View File

@ -0,0 +1,54 @@
using System;
using LibHac.Common;
using LibHac.FsSrv.Sf;
using LibHac.FsSrv.Storage.Sf;
namespace LibHac.SdmmcSrv;
public class MmcManager : IStorageDeviceManager
{
public MmcManager()
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
public Result IsInserted(out bool isInserted)
{
throw new NotImplementedException();
}
public Result IsHandleValid(out bool isValid, uint handle)
{
throw new NotImplementedException();
}
public Result OpenDetectionEvent(ref SharedRef<IEventNotifier> outDetectionEvent)
{
throw new NotImplementedException();
}
public Result OpenOperator(ref SharedRef<IStorageDeviceOperator> outDeviceOperator)
{
throw new NotImplementedException();
}
public Result OpenDevice(ref SharedRef<IStorageDevice> outStorageDevice, ulong attribute)
{
throw new NotImplementedException();
}
public Result OpenStorage(ref SharedRef<IStorage> outStorage, ulong attribute)
{
throw new NotImplementedException();
}
public Result Invalidate()
{
throw new NotImplementedException();
}
}

View File

@ -0,0 +1,19 @@
using System;
using LibHac.FsSystem;
namespace LibHac.SdmmcSrv;
internal class SdCardDeviceDetectionEventManager : CardDeviceDetectionEventManager
{
public SdCardDeviceDetectionEventManager(Sdmmc.Port port)
{
CallbackArgs.Port = port;
throw new NotImplementedException();
}
public override void Dispose()
{
throw new NotImplementedException();
}
}

View File

@ -0,0 +1,54 @@
using System;
using LibHac.Common;
using LibHac.FsSrv.Sf;
using LibHac.FsSrv.Storage.Sf;
namespace LibHac.SdmmcSrv;
public class SdCardManager : IStorageDeviceManager
{
public SdCardManager()
{
}
public void Dispose()
{
throw new NotImplementedException();
}
public Result IsInserted(out bool isInserted)
{
throw new NotImplementedException();
}
public Result IsHandleValid(out bool isValid, uint handle)
{
throw new NotImplementedException();
}
public Result OpenDetectionEvent(ref SharedRef<IEventNotifier> outDetectionEvent)
{
throw new NotImplementedException();
}
public Result OpenOperator(ref SharedRef<IStorageDeviceOperator> outDeviceOperator)
{
throw new NotImplementedException();
}
public Result OpenDevice(ref SharedRef<IStorageDevice> outStorageDevice, ulong attribute)
{
throw new NotImplementedException();
}
public Result OpenStorage(ref SharedRef<IStorage> outStorage, ulong attribute)
{
throw new NotImplementedException();
}
public Result Invalidate()
{
throw new NotImplementedException();
}
}

View File

@ -17,6 +17,11 @@ public readonly ref struct InBuffer
_buffer = buffer;
}
public ref readonly T As<T>() where T : unmanaged
{
return ref SpanHelpers.AsReadOnlyStruct<T>(_buffer);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static InBuffer FromSpan<T>(ReadOnlySpan<T> buffer) where T : unmanaged
{
@ -42,6 +47,11 @@ public readonly ref struct OutBuffer
_buffer = buffer;
}
public ref T As<T>() where T : unmanaged
{
return ref SpanHelpers.AsStruct<T>(_buffer);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static OutBuffer FromSpan<T>(Span<T> buffer) where T : unmanaged
{

View File

@ -46,7 +46,7 @@ public static class FileSystemServerFactory
var config = new FileSystemServerConfig();
config.FsCreators = defaultObjects.FsCreators;
config.DeviceOperator = defaultObjects.DeviceOperator;
config.StorageDeviceManagerFactory = defaultObjects.StorageDeviceManagerFactory;
config.ExternalKeySet = new ExternalKeySet();
config.RandomGenerator = randomGenerator;

View File

@ -26,7 +26,7 @@ public static class HorizonFactory
var config = new FileSystemServerConfig();
config.FsCreators = defaultObjects.FsCreators;
config.DeviceOperator = defaultObjects.DeviceOperator;
config.StorageDeviceManagerFactory = defaultObjects.StorageDeviceManagerFactory;
config.ExternalKeySet = new ExternalKeySet();
config.RandomGenerator = randomGenerator;