Add GameCardDeviceOperator

This commit is contained in:
Alex Barney 2022-05-23 13:09:42 -07:00
parent f4c59771cb
commit e18f6a9daf
10 changed files with 474 additions and 155 deletions

View file

@ -1,6 +1,7 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSrv.Storage.Sf;
using LibHac.Gc;
using LibHac.GcSrv;
using LibHac.Os;
using LibHac.SdmmcSrv;
@ -22,6 +23,7 @@ public class EmulatedStorageDeviceManagerFactory : IStorageDeviceManagerFactory
private readonly bool _hasGameCard;
private readonly FileSystemServer _fsServer;
private readonly GameCardDummy _gc;
public EmulatedStorageDeviceManagerFactory(FileSystemServer fsServer, bool hasGameCard)
{
@ -138,12 +140,13 @@ public class EmulatedStorageDeviceManagerFactory : IStorageDeviceManagerFactory
{
if (_hasGameCard)
{
using SharedRef<GameCardManager> manger = GameCardManager.CreateShared(_fsServer);
_gameCardDeviceManager.SetByMove(ref manger.Ref);
using SharedRef<GameCardManager> manager = GameCardManager.CreateShared(_gc, _fsServer);
_gameCardDeviceManager.SetByMove(ref manager.Ref);
}
else
{
_dummyGameCardDeviceManager.Reset(new DummyGameCardManager());
using SharedRef<DummyGameCardManager> manager = DummyGameCardManager.CreateShared();
_dummyGameCardDeviceManager.SetByMove(ref manager.Ref);
}
}
}

View file

@ -2,10 +2,26 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSrv.Sf;
using LibHac.Os;
using LibHac.Sf;
using IDirectory = LibHac.Fs.Fsa.IDirectory;
using IFile = LibHac.Fs.Fsa.IFile;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
using Path = LibHac.Fs.Path;
namespace LibHac.FsSystem;
public class DummyEventNotifier : IEventNotifier
{
public void Dispose() { }
public Result GetEventHandle(out NativeHandle handle)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Various utility functions used by the <see cref="LibHac.FsSystem"/> namespace.
/// </summary>

View file

@ -9,4 +9,6 @@ public static class Values
public static int GcCardImageHashSize => 0x20;
public static int GcDeviceCertificateSize => 0x200;
public static int GcCardKeyAreaSize => 0x1000;
public static int GcCardKeyAreaPageCount => 8;
public static int GcCertAreaStartPageAddress => 56;
}

View file

@ -4,21 +4,38 @@ using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSrv.Sf;
using LibHac.FsSrv.Storage.Sf;
using LibHac.FsSystem;
using LibHac.Sf;
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
namespace LibHac.GcSrv;
public class DummyGameCardManager : IStorageDeviceManager, IStorageDeviceOperator
public class DummyGameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IGameCardKeyManager
{
public DummyGameCardManager()
private SharedRef<DummyEventNotifier> _eventNotifier;
// LibHac additions
private WeakRef<DummyGameCardManager> _selfReference;
private DummyGameCardManager()
{
throw new NotImplementedException();
_eventNotifier = new SharedRef<DummyEventNotifier>(new DummyEventNotifier());
}
public static SharedRef<DummyGameCardManager> CreateShared()
{
var manager = new DummyGameCardManager();
using var sharedManager = new SharedRef<DummyGameCardManager>(manager);
manager._selfReference.Set(in sharedManager);
return SharedRef<DummyGameCardManager>.CreateMove(ref sharedManager.Ref);
}
public void Dispose()
{
throw new NotImplementedException();
_eventNotifier.Destroy();
_selfReference.Destroy();
}
public Result IsInserted(out bool isInserted)
@ -35,12 +52,20 @@ public class DummyGameCardManager : IStorageDeviceManager, IStorageDeviceOperato
public Result OpenDetectionEvent(ref SharedRef<IEventNotifier> outDetectionEvent)
{
throw new NotImplementedException();
outDetectionEvent.SetByCopy(in _eventNotifier);
return Result.Success;
}
public Result OpenOperator(ref SharedRef<IStorageDeviceOperator> outDeviceOperator)
{
throw new NotImplementedException();
using SharedRef<DummyGameCardManager> deviceOperator = SharedRef<DummyGameCardManager>.Create(in _selfReference);
if (!deviceOperator.HasValue)
return ResultFs.AllocationMemoryFailedInGameCardManagerG.Log();
outDeviceOperator.SetByMove(ref deviceOperator.Ref);
return Result.Success;
}
public Result OpenDevice(ref SharedRef<IStorageDevice> outStorageDevice, ulong attribute)
@ -157,7 +182,7 @@ public class DummyGameCardManager : IStorageDeviceManager, IStorageDeviceOperato
{
case GameCardManagerOperationIdValue.IsGameCardActivationValid:
{
if (outBuffer.Size < Unsafe.SizeOf<bool>())
if (outBuffer.Size < sizeof(bool))
return ResultFs.InvalidArgument.Log();
outBuffer.As<bool>() = false;
@ -186,4 +211,9 @@ public class DummyGameCardManager : IStorageDeviceManager, IStorageDeviceOperato
return ResultFs.NotImplemented.Log();
}
public void PresetInternalKeys(ReadOnlySpan<byte> gameCardKey, ReadOnlySpan<byte> gameCardCertificate)
{
// Empty
}
}

View file

@ -3,9 +3,9 @@ using LibHac.FsSystem;
namespace LibHac.GcSrv;
internal class GameCardDeviceDetectionEventManager : CardDeviceDetectionEventManager
internal class GameCardDetectionEventManager : CardDeviceDetectionEventManager
{
public GameCardDeviceDetectionEventManager()
public GameCardDetectionEventManager()
{
throw new NotImplementedException();
}

View file

@ -0,0 +1,196 @@
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSrv.Storage.Sf;
using LibHac.Gc;
using LibHac.Gc.Writer;
using LibHac.Os;
using LibHac.Sf;
using static LibHac.Gc.Values;
namespace LibHac.GcSrv;
internal class GameCardDeviceOperator : IStorageDeviceOperator
{
private SharedRef<GameCardStorageDevice> _storageDevice;
// LibHac additions
private readonly GameCardDummy _gc;
public static uint BytesToPages(long byteCount)
{
return (uint)((ulong)byteCount / (ulong)GcPageSize);
}
public GameCardDeviceOperator(ref SharedRef<GameCardStorageDevice> storageDevice, GameCardDummy gc)
{
_storageDevice = SharedRef<GameCardStorageDevice>.CreateMove(ref storageDevice);
_gc = gc;
}
public void Dispose()
{
_storageDevice.Destroy();
}
public Result Operate(int operationId)
{
return ResultFs.NotImplemented.Log();
}
public Result OperateIn(InBuffer buffer, long offset, long size, int operationId)
{
Result result;
var operation = (GameCardOperationIdValue)operationId;
switch (operation)
{
case GameCardOperationIdValue.EraseGameCard:
{
using var readLock = new SharedLock<ReaderWriterLock>();
Result res = _storageDevice.Get.AcquireReadLock(ref readLock.Ref());
if (res.IsFailure()) return res.Miss();
if (buffer.Size != sizeof(long))
return ResultFs.InvalidArgument.Log();
result = _gc.Writer.EraseAndWriteParameter((MemorySize)size, BytesToPages(buffer.As<long>()));
break;
}
default:
return ResultFs.InvalidArgument.Log();
}
return _storageDevice.Get.HandleGameCardAccessResult(result).Ret();
}
public Result OperateOut(out long bytesWritten, OutBuffer buffer, int operationId)
{
Result result;
bytesWritten = 0;
var operation = (GameCardOperationIdValue)operationId;
using (var readLock = new SharedLock<ReaderWriterLock>())
{
switch (operation)
{
case GameCardOperationIdValue.GetGameCardIdSet:
{
Result res = _storageDevice.Get.AcquireReadLock(ref readLock.Ref());
if (res.IsFailure()) return res.Miss();
if (buffer.Size < Unsafe.SizeOf<GameCardIdSet>())
return ResultFs.InvalidArgument.Log();
result = _gc.GetGameCardIdSet(out buffer.As<GameCardIdSet>());
bytesWritten = Unsafe.SizeOf<GameCardIdSet>();
break;
}
case GameCardOperationIdValue.GetGameCardDeviceId:
{
Result res = _storageDevice.Get.AcquireReadLock(ref readLock.Ref());
if (res.IsFailure()) return res.Miss();
if (buffer.Size < GcCardDeviceIdSize)
return ResultFs.InvalidArgument.Log();
result = _gc.GetCardDeviceId(buffer.Buffer);
bytesWritten = GcCardDeviceIdSize;
break;
}
case GameCardOperationIdValue.GetGameCardImageHash:
{
Result res = _storageDevice.Get.AcquireReadLock(ref readLock.Ref());
if (res.IsFailure()) return res.Miss();
if (buffer.Size < GcCardImageHashSize)
return ResultFs.InvalidArgument.Log();
result = _gc.GetCardImageHash(buffer.Buffer);
bytesWritten = GcCardImageHashSize;
break;
}
case GameCardOperationIdValue.GetGameCardDeviceCertificate:
{
Result res = _storageDevice.Get.AcquireReadLock(ref readLock.Ref());
if (res.IsFailure()) return res.Miss();
if (buffer.Size < GcDeviceCertificateSize)
return ResultFs.InvalidArgument.Log();
result = _gc.GetCardDeviceCertificate(buffer.Buffer);
bytesWritten = GcDeviceCertificateSize;
break;
}
case GameCardOperationIdValue.GetGameCardStatus:
{
Result res = _storageDevice.Get.AcquireReadLock(ref readLock.Ref());
if (res.IsFailure()) return res.Miss();
if (buffer.Size < Unsafe.SizeOf<GameCardStatus>())
return ResultFs.InvalidArgument.Log();
result = _gc.GetCardStatus(out buffer.As<GameCardStatus>());
bytesWritten = Unsafe.SizeOf<GameCardStatus>();
break;
}
default:
return ResultFs.InvalidArgument.Log();
}
}
return _storageDevice.Get.HandleGameCardAccessResult(result).Ret();
}
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)
{
UnsafeHelpers.SkipParamInit(out bytesWritten);
return ResultFs.NotImplemented.Log();
}
public Result OperateIn2Out(out long bytesWritten, OutBuffer outBuffer, InBuffer inBuffer1, InBuffer inBuffer2,
long offset, long size, int operationId)
{
Result result;
bytesWritten = 0;
var operation = (GameCardOperationIdValue)operationId;
switch (operation)
{
case GameCardOperationIdValue.ChallengeCardExistence:
{
using var readLock = new SharedLock<ReaderWriterLock>();
Result res = _storageDevice.Get.AcquireReadLock(ref readLock.Ref());
if (res.IsFailure()) return res.Miss();
if (outBuffer.Size < GcCardExistenceResponseDataSize)
return ResultFs.InvalidArgument.Log();
result = _gc.ChallengeCardExistence(outBuffer.Buffer, inBuffer1.Buffer, inBuffer2.Buffer);
bytesWritten = GcCardExistenceResponseDataSize;
break;
}
default:
return ResultFs.InvalidArgument.Log();
}
return _storageDevice.Get.HandleGameCardAccessResult(result).Ret();
}
}

View file

@ -12,6 +12,8 @@ using LibHac.Gc.Impl;
using LibHac.Gc.Writer;
using LibHac.Os;
using LibHac.Sf;
using static LibHac.Gc.Values;
using static LibHac.GcSrv.GameCardDeviceOperator;
using IStorage = LibHac.FsSrv.Sf.IStorage;
namespace LibHac.GcSrv;
@ -31,23 +33,24 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
private bool _isFinalized;
private CardState _state;
private GameCardHandle _currentHandle;
private GameCardDeviceDetectionEventManager _detectionEventManager;
private GameCardDetectionEventManager _detectionEventManager;
// LibHac additions
private WeakRef<GameCardManager> _selfReference;
private readonly FileSystemServer _fsServer;
private readonly GameCardDummy _gc;
private GameCardManager(FileSystemServer fsServer)
private GameCardManager(GameCardDummy gc, FileSystemServer fsServer)
{
_rwLock = new ReaderWriterLock(fsServer.Hos.Os);
_fsServer = fsServer;
_gc = gc;
}
public static SharedRef<GameCardManager> CreateShared(FileSystemServer fsServer)
public static SharedRef<GameCardManager> CreateShared(GameCardDummy gc, FileSystemServer fsServer)
{
var manager = new GameCardManager(fsServer);
var manager = new GameCardManager(gc, fsServer);
using var sharedManager = new SharedRef<GameCardManager>(manager);
manager._selfReference.Set(in sharedManager);
@ -62,11 +65,8 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
_rwLock?.Dispose();
_rwLock = null;
}
public static uint BytesToPages(long byteCount)
{
return (uint)((ulong)byteCount / (ulong)Values.GcPageSize);
_selfReference.Destroy();
}
private void DeactivateAndChangeState()
@ -102,6 +102,16 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
return HandleGameCardAccessResult(_gc.SetCardToSecureMode());
}
private Result LockAndHandleGameCardAccessResult(Result result)
{
if (result.IsSuccess())
return Result.Success;
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
return HandleGameCardAccessResult(result).Ret();
}
public Result IsInserted(out bool isInserted)
{
UnsafeHelpers.SkipParamInit(out isInserted);
@ -129,7 +139,7 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
_gc.Initialize(default, default);
// Missing: Register the device buffer
_detectionEventManager = new GameCardDeviceDetectionEventManager();
_detectionEventManager = new GameCardDetectionEventManager();
_isInitialized = true;
return Result.Success;
@ -371,7 +381,9 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
public Result PutToSleep()
{
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
_gc.PutToSleep();
if (_isInitialized)
_gc.PutToSleep();
return Result.Success;
}
@ -379,7 +391,9 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
public Result Awaken()
{
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
_gc.Awaken();
if (_isInitialized)
_gc.Awaken();
return Result.Success;
}
@ -387,7 +401,9 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
public Result Shutdown()
{
using var writeLock = new UniqueLock<ReaderWriterLock>(_rwLock);
_gc.PutToSleep();
if (_isInitialized)
_gc.PutToSleep();
return Result.Success;
}
@ -438,13 +454,15 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
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();
@ -452,6 +470,7 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
if (res.IsFailure()) return res.Miss();
return Result.Success;
}
default:
return ResultFs.InvalidArgument.Log();
@ -482,6 +501,7 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
return Result.Success;
}
case GameCardManagerOperationIdValue.GetGameCardErrorInfo:
{
if (buffer.Size < Unsafe.SizeOf<GameCardErrorInfo>())
return ResultFs.InvalidArgument.Log();
@ -490,8 +510,9 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
bytesWritten = Unsafe.SizeOf<GameCardErrorInfo>();
return Result.Success;
}
case GameCardManagerOperationIdValue.GetGameCardErrorReportInfo:
{
if (buffer.Size < Unsafe.SizeOf<GameCardErrorReportInfo>())
return ResultFs.InvalidArgument.Log();
@ -500,8 +521,9 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
bytesWritten = Unsafe.SizeOf<GameCardErrorReportInfo>();
return Result.Success;
}
case GameCardManagerOperationIdValue.ReadParamDirectly:
{
if (buffer.Size < Unsafe.SizeOf<DevCardParameter>())
return ResultFs.InvalidArgument.Log();
@ -510,6 +532,7 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
bytesWritten = Unsafe.SizeOf<DevCardParameter>();
return Result.Success;
}
default:
return ResultFs.InvalidArgument.Log();
@ -536,6 +559,7 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
switch (operation)
{
case GameCardManagerOperationIdValue.IsGameCardActivationValid:
{
if (inBuffer.Size != sizeof(GameCardHandle))
return ResultFs.InvalidArgument.Log();
@ -546,9 +570,10 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
bytesWritten = sizeof(bool);
return Result.Success;
}
case GameCardManagerOperationIdValue.GetGameCardAsicInfo:
if (inBuffer.Size != Values.GcAsicFirmwareSize)
{
if (inBuffer.Size != GcAsicFirmwareSize)
return ResultFs.InvalidArgument.Log();
if (outBuffer.Size < Unsafe.SizeOf<RmaInformation>())
@ -561,20 +586,22 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
bytesWritten = Unsafe.SizeOf<RmaInformation>();
return Result.Success;
}
case GameCardManagerOperationIdValue.GetGameCardDeviceIdForProdCard:
if (inBuffer.Size < Values.GcPageSize)
{
if (inBuffer.Size < GcPageSize)
return ResultFs.InvalidArgument.Log();
if (outBuffer.Size < Values.GcPageSize)
if (outBuffer.Size < GcPageSize)
return ResultFs.InvalidArgument.Log();
res = GetGameCardDeviceIdForProdCard(outBuffer.Buffer, inBuffer.Buffer);
if (res.IsFailure()) return res.Miss();
bytesWritten = Values.GcPageSize;
bytesWritten = GcPageSize;
return Result.Success;
}
case GameCardManagerOperationIdValue.WriteToGameCardDirectly:
return WriteToGameCardDirectly(offset, outBuffer.Buffer.Slice(0, (int)size)).Ret();
@ -601,6 +628,7 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
_gc.UnregisterDetectionEventCallback();
_isFinalized = true;
_gc.FinalizeGc();
// nn::fssystem::PooledBuffer::Deallocate
// nn::gc::UnregisterDeviceVirtualAddress
}
}
@ -650,7 +678,7 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
{
UnsafeHelpers.SkipParamInit(out outRmaInfo);
Assert.SdkRequiresEqual(asicFirmwareBuffer.Length, Values.GcAsicFirmwareSize);
Assert.SdkRequiresEqual(asicFirmwareBuffer.Length, GcAsicFirmwareSize);
_gc.Writer.SetUserAsicFirmwareBuffer(asicFirmwareBuffer);
_gc.Writer.ChangeMode(AsicMode.Write);
@ -664,36 +692,36 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
private Result GetGameCardDeviceIdForProdCard(Span<byte> outBuffer, ReadOnlySpan<byte> devHeaderBuffer)
{
Assert.SdkRequiresGreaterEqual(outBuffer.Length, Values.GcPageSize);
Assert.SdkRequiresGreaterEqual(devHeaderBuffer.Length, Values.GcPageSize);
Assert.SdkRequiresGreaterEqual(outBuffer.Length, GcPageSize);
Assert.SdkRequiresGreaterEqual(devHeaderBuffer.Length, 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);
int writeSize = GcPageSize;
using 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();
Span<byte> originalHeaderBuffer = stackalloc byte[writeSize];
originalHeaderBuffer.Clear();
_gc.GetCardHeader(pooledBuffer.GetBuffer());
if (res.IsFailure()) return res.Miss();
pooledBuffer.GetBuffer().CopyTo(tmpBuffer);
pooledBuffer.GetBuffer().CopyTo(originalHeaderBuffer);
// Write the provided card header
_gc.Writer.ChangeMode(AsicMode.Write);
res = HandleGameCardAccessResult(_gc.Writer.ActivateForWriter());
res = ActivateGameCardForWriter();
if (res.IsFailure()) return res.Miss();
devHeaderBuffer.CopyTo(pooledBuffer.GetBuffer());
res = _gc.Writer.Write(pooledBuffer.GetBuffer(), 8, 1);
res = _gc.Writer.Write(pooledBuffer.GetBuffer(), (uint)GcCardKeyAreaPageCount, 1);
if (res.IsFailure()) return res.Miss();
// Read the cert area
@ -701,7 +729,7 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
res = _gc.Activate();
if (res.IsFailure()) return res.Miss();
res = _gc.Read(pooledBuffer.GetBuffer(), 0x38, 1);
res = _gc.Read(pooledBuffer.GetBuffer(), (uint)GcCertAreaStartPageAddress, 1);
if (res.IsFailure()) return res.Miss();
Span<byte> deviceCert = stackalloc byte[writeSize];
@ -709,11 +737,11 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
// Restore the original card header
_gc.Writer.ChangeMode(AsicMode.Write);
res = HandleGameCardAccessResult(_gc.Writer.ActivateForWriter());
res = ActivateGameCardForWriter();
if (res.IsFailure()) return res.Miss();
tmpBuffer.CopyTo(pooledBuffer.GetBuffer());
res = _gc.Writer.Write(pooledBuffer.GetBuffer(), 8, 1);
originalHeaderBuffer.CopyTo(pooledBuffer.GetBuffer());
res = _gc.Writer.Write(pooledBuffer.GetBuffer(), (uint)GcCardKeyAreaPageCount, 1);
if (res.IsFailure()) return res.Miss();
deviceCert.CopyTo(outBuffer);
@ -749,25 +777,17 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
private Result WriteToGameCardDirectly(long offset, Span<byte> buffer)
{
Result res;
Result result;
using (new SharedLock<ReaderWriterLock>(_rwLock))
{
if (buffer.Length == 0)
return Result.Success;
res = _gc.Writer.Write(buffer, BytesToPages(offset), BytesToPages(buffer.Length));
result = _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;
return LockAndHandleGameCardAccessResult(result).Ret();
}
private Result ForceEraseGameCard()
@ -786,24 +806,26 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
public Result AcquireReadLock(ref SharedLock<ReaderWriterLock> outLock, GameCardHandle handle)
{
using var readLock = new SharedLock<ReaderWriterLock>(_rwLock);
if (_state != CardState.Initial && !_gc.IsCardActivationValid())
using (var readLock = new SharedLock<ReaderWriterLock>(_rwLock))
{
readLock.Unlock();
Invalidate().IgnoreResult();
if (_state == CardState.Initial || _gc.IsCardActivationValid())
{
if (_currentHandle == handle)
{
outLock.Set(ref readLock.Ref());
return Result.Success;
}
return ResultFs.GameCardFsCheckHandleInAcquireReadLock.Log();
return ResultFs.GameCardFsCheckHandleInAcquireReadLock.Log();
}
}
if (_currentHandle != handle)
return ResultFs.GameCardFsCheckHandleInAcquireReadLock.Log();
Invalidate().IgnoreResult();
outLock.Set(ref readLock.Ref());
return Result.Success;
return ResultFs.GameCardFsCheckHandleInAcquireReadLock.Log();
}
public Result AcquireSecureLock(ref SharedLock<ReaderWriterLock> outLock, ref GameCardHandle handle,
public Result AcquireSecureLock(ref SharedLock<ReaderWriterLock> outLock, ref GameCardHandle inOutHandle,
ReadOnlySpan<byte> cardDeviceId, ReadOnlySpan<byte> cardImageHash)
{
using (var readLock = new SharedLock<ReaderWriterLock>(_rwLock))
@ -818,7 +840,7 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
readLock.Unlock();
Invalidate().IgnoreResult();
}
else if (_currentHandle == handle)
else if (_currentHandle == inOutHandle)
{
outLock.Set(ref readLock.Ref());
return Result.Success;
@ -834,8 +856,8 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
return ResultFs.GameCardFsCheckModeInAcquireSecureLock.Log();
}
Span<byte> currentCardDeviceId = stackalloc byte[Values.GcCardDeviceIdSize];
Span<byte> currentCardImageHash = stackalloc byte[Values.GcCardImageHashSize];
Span<byte> currentCardDeviceId = stackalloc byte[GcCardDeviceIdSize];
Span<byte> currentCardImageHash = stackalloc byte[GcCardImageHashSize];
Result res = HandleGameCardAccessResult(_gc.GetCardDeviceId(currentCardDeviceId));
if (res.IsFailure()) return res.Miss();
@ -843,8 +865,8 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
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))
if (!Crypto.CryptoUtil.IsSameBytes(currentCardDeviceId, cardDeviceId, GcCardDeviceIdSize) ||
!Crypto.CryptoUtil.IsSameBytes(currentCardImageHash, cardImageHash, GcCardImageHashSize))
return ResultFs.GameCardFsCheckModeInAcquireSecureLock.Log();
res = GetHandle(out newHandle);
@ -856,7 +878,7 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
Result res = AcquireReadLock(ref readLock.Ref(), newHandle);
if (res.IsFailure()) return res.Miss();
handle = newHandle;
inOutHandle = newHandle;
outLock.Set(ref readLock.Ref());
return Result.Success;
@ -876,6 +898,8 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
public Result HandleGameCardAccessResult(Result result)
{
Assert.SdkRequires(_rwLock.IsWriteLockHeldByCurrentThread());
if (result.IsFailure())
{
DeactivateAndChangeState();

View file

@ -4,7 +4,10 @@ using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.Gc;
using LibHac.Sf;
using static LibHac.Gc.Values;
using static LibHac.GcSrv.GameCardDeviceOperator;
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
namespace LibHac.GcSrv;
@ -38,8 +41,7 @@ internal class ReadOnlyGameCardStorage : IStorage
// Missing: Allocate a device buffer if the destination buffer is not one
return _gc.Read(destination, GameCardManager.BytesToPages(offset),
GameCardManager.BytesToPages(destination.Length)).Ret();
return _gc.Read(destination, BytesToPages(offset), BytesToPages(destination.Length)).Ret();
}
public override Result Write(long offset, ReadOnlySpan<byte> source)
@ -123,8 +125,7 @@ internal class WriteOnlyGameCardStorage : IStorage
// Missing: Allocate a device buffer if the destination buffer is not one
return _gc.Writer.Write(source, GameCardManager.BytesToPages(offset),
GameCardManager.BytesToPages(source.Length)).Ret();
return _gc.Writer.Write(source, BytesToPages(offset), BytesToPages(source.Length)).Ret();
}
public override Result Flush()
@ -153,4 +154,52 @@ internal class WriteOnlyGameCardStorage : IStorage
{
return ResultFs.NotImplemented.Log();
}
}
internal abstract class GameCardStorageInterfaceAdapter : IStorageSf
{
private SharedRef<IStorage> _baseStorage;
protected GameCardStorageInterfaceAdapter(ref SharedRef<IStorage> baseStorage)
{
_baseStorage = SharedRef<IStorage>.CreateMove(ref baseStorage);
}
public virtual void Dispose()
{
_baseStorage.Destroy();
}
public virtual Result Read(long offset, OutBuffer destination, long size)
{
return _baseStorage.Get.Read(offset, destination.Buffer.Slice(0, (int)size)).Ret();
}
public virtual Result Write(long offset, InBuffer source, long size)
{
return _baseStorage.Get.Write(offset, source.Buffer.Slice(0, (int)size)).Ret();
}
public virtual Result Flush()
{
return _baseStorage.Get.Flush().Ret();
}
public virtual Result SetSize(long size)
{
return _baseStorage.Get.SetSize(size).Ret();
}
public virtual Result GetSize(out long size)
{
return _baseStorage.Get.GetSize(out size).Ret();
}
public virtual Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size)
{
UnsafeHelpers.SkipParamInit(out rangeInfo);
return _baseStorage.Get.OperateRange(SpanHelpers.AsByteSpan(ref rangeInfo), (OperationId)operationId, offset,
size, ReadOnlySpan<byte>.Empty).Ret();
}
}

View file

@ -7,58 +7,9 @@ using LibHac.FsSrv.Storage.Sf;
using LibHac.Gc;
using LibHac.Os;
using LibHac.Sf;
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
namespace LibHac.GcSrv;
internal abstract class GameCardStorageInterfaceAdapter : IStorageSf
{
private SharedRef<IStorage> _baseStorage;
protected GameCardStorageInterfaceAdapter(ref SharedRef<IStorage> baseStorage)
{
_baseStorage = SharedRef<IStorage>.CreateMove(ref baseStorage);
}
public virtual void Dispose()
{
_baseStorage.Destroy();
}
public virtual Result Read(long offset, OutBuffer destination, long size)
{
return _baseStorage.Get.Read(offset, destination.Buffer.Slice(0, (int)size)).Ret();
}
public virtual Result Write(long offset, InBuffer source, long size)
{
return _baseStorage.Get.Write(offset, source.Buffer.Slice(0, (int)size)).Ret();
}
public virtual Result Flush()
{
return _baseStorage.Get.Flush().Ret();
}
public virtual Result SetSize(long size)
{
return _baseStorage.Get.SetSize(size).Ret();
}
public virtual Result GetSize(out long size)
{
return _baseStorage.Get.GetSize(out size).Ret();
}
public virtual Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size)
{
UnsafeHelpers.SkipParamInit(out rangeInfo);
return _baseStorage.Get.OperateRange(SpanHelpers.AsByteSpan(ref rangeInfo), (OperationId)operationId, offset,
size, ReadOnlySpan<byte>.Empty).Ret();
}
}
internal class GameCardStorageDevice : GameCardStorageInterfaceAdapter, IStorageDevice
{
private SharedRef<IGameCardManager> _manager;
@ -67,16 +18,23 @@ internal class GameCardStorageDevice : GameCardStorageInterfaceAdapter, IStorage
private Array16<byte> _cardDeviceId;
private Array32<byte> _cardImageHash;
public GameCardStorageDevice(ref SharedRef<IGameCardManager> manager, ref SharedRef<IStorage> baseStorage,
GameCardHandle handle) : base(ref baseStorage)
// LibHac additions
private WeakRef<GameCardStorageDevice> _selfReference;
private readonly GameCardDummy _gc;
private GameCardStorageDevice(GameCardDummy gc, ref SharedRef<IGameCardManager> manager,
ref SharedRef<IStorage> baseStorage, GameCardHandle handle) : base(ref baseStorage)
{
_manager = SharedRef<IGameCardManager>.CreateMove(ref manager);
_handle = handle;
_isSecure = false;
_gc = gc;
}
public GameCardStorageDevice(ref SharedRef<IGameCardManager> manager, ref SharedRef<IStorage> baseStorage,
GameCardHandle handle, bool isSecure, ReadOnlySpan<byte> cardDeviceId, ReadOnlySpan<byte> cardImageHash)
private GameCardStorageDevice(GameCardDummy gc, ref SharedRef<IGameCardManager> manager,
ref SharedRef<IStorage> baseStorage, GameCardHandle handle, bool isSecure, ReadOnlySpan<byte> cardDeviceId,
ReadOnlySpan<byte> cardImageHash)
: base(ref baseStorage)
{
Assert.SdkRequiresEqual(cardDeviceId.Length, Values.GcCardDeviceIdSize);
@ -88,27 +46,49 @@ internal class GameCardStorageDevice : GameCardStorageInterfaceAdapter, IStorage
cardDeviceId.CopyTo(_cardDeviceId.Items);
cardImageHash.CopyTo(_cardImageHash.Items);
_gc = gc;
}
public static SharedRef<GameCardStorageDevice> CreateShared(GameCardDummy gc,
ref SharedRef<IGameCardManager> manager, ref SharedRef<IStorage> baseStorage, GameCardHandle handle)
{
var storageDevice = new GameCardStorageDevice(gc, ref manager, ref baseStorage, handle);
using var sharedStorageDevice = new SharedRef<GameCardStorageDevice>(storageDevice);
storageDevice._selfReference.Set(in sharedStorageDevice);
return SharedRef<GameCardStorageDevice>.CreateMove(ref sharedStorageDevice.Ref);
}
public static SharedRef<GameCardStorageDevice> CreateShared(GameCardDummy gc,
ref SharedRef<IGameCardManager> manager, ref 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,
cardImageHash);
using var sharedStorageDevice = new SharedRef<GameCardStorageDevice>(storageDevice);
storageDevice._selfReference.Set(in sharedStorageDevice);
return SharedRef<GameCardStorageDevice>.CreateMove(ref sharedStorageDevice.Ref);
}
public override void Dispose()
{
_manager.Destroy();
_selfReference.Destroy();
base.Dispose();
}
public Result AcquireReadLock(ref SharedLock<ReaderWriterLock> outLock)
{
if (_isSecure)
{
Result res = _manager.Get.AcquireSecureLock(ref outLock, ref _handle, _cardDeviceId, _cardImageHash);
if (res.IsFailure()) return res.Miss();
}
else
{
Result res = _manager.Get.AcquireReadLock(ref outLock, _handle);
if (res.IsFailure()) return res.Miss();
}
Result res = _isSecure
? _manager.Get.AcquireSecureLock(ref outLock, ref _handle, _cardDeviceId, _cardImageHash)
: _manager.Get.AcquireReadLock(ref outLock, _handle);
if (res.IsFailure()) return res.Miss();
return Result.Success;
}
@ -118,11 +98,23 @@ internal class GameCardStorageDevice : GameCardStorageInterfaceAdapter, IStorage
return _manager.Get.AcquireWriteLock(ref outLock).Ret();
}
public Result HandleGameCardAccessResult(Result result)
private Result HandleGameCardAccessResultImpl(Result result)
{
return _manager.Get.HandleGameCardAccessResult(result);
}
public Result HandleGameCardAccessResult(Result result)
{
if (result.IsSuccess())
return Result.Success;
using var writeLock = new UniqueLock<ReaderWriterLock>();
Result res = AcquireWriteLock(ref writeLock.Ref());
if (res.IsFailure()) return res.Miss();
return HandleGameCardAccessResultImpl(result).Ret();
}
public Result GetHandle(out GameCardHandle handle)
{
handle = _handle;
@ -140,7 +132,23 @@ internal class GameCardStorageDevice : GameCardStorageInterfaceAdapter, IStorage
public Result OpenOperator(ref SharedRef<IStorageDeviceOperator> outDeviceOperator)
{
throw new NotImplementedException();
using var readLock = new SharedLock<ReaderWriterLock>();
Result res = AcquireReadLock(ref readLock.Ref());
if (res.IsFailure()) return res.Miss();
using SharedRef<GameCardStorageDevice> storageDevice =
SharedRef<GameCardStorageDevice>.Create(in _selfReference);
using var deviceOperator =
new SharedRef<GameCardDeviceOperator>(new GameCardDeviceOperator(ref storageDevice.Ref, _gc));
if (!deviceOperator.HasValue)
return ResultFs.AllocationMemoryFailedInGameCardManagerG.Log();
outDeviceOperator.SetByMove(ref deviceOperator.Ref);
return Result.Success;
}
public override Result Read(long offset, OutBuffer destination, long size)
@ -182,15 +190,6 @@ internal class GameCardStorageDevice : GameCardStorageInterfaceAdapter, IStorage
resultGetSize = base.GetSize(out size);
}
if (resultGetSize.IsSuccess())
return Result.Success;
using (var writeLock = new UniqueLock<ReaderWriterLock>())
{
Result res = AcquireWriteLock(ref writeLock.Ref());
if (res.IsFailure()) return res.Miss();
return HandleGameCardAccessResult(resultGetSize).Ret();
}
return HandleGameCardAccessResult(resultGetSize).Ret();
}
}

View file

@ -6,7 +6,7 @@ namespace LibHac.GcSrv;
internal interface IGameCardManager : IDisposable
{
Result AcquireReadLock(ref SharedLock<ReaderWriterLock> outLock, GameCardHandle handle);
Result AcquireSecureLock(ref SharedLock<ReaderWriterLock> outLock, ref GameCardHandle handle, ReadOnlySpan<byte> cardDeviceId, ReadOnlySpan<byte> cardImageHash);
Result AcquireSecureLock(ref SharedLock<ReaderWriterLock> outLock, ref GameCardHandle inOutHandle, ReadOnlySpan<byte> cardDeviceId, ReadOnlySpan<byte> cardImageHash);
Result AcquireWriteLock(ref UniqueLock<ReaderWriterLock> outLock);
Result HandleGameCardAccessResult(Result result);
Result GetHandle(out GameCardHandle outHandle);