diff --git a/src/LibHac/FsSrv/Storage/EmulatedStorageDeviceManagerFactory.cs b/src/LibHac/FsSrv/Storage/EmulatedStorageDeviceManagerFactory.cs index 2e3a1902..685cac8d 100644 --- a/src/LibHac/FsSrv/Storage/EmulatedStorageDeviceManagerFactory.cs +++ b/src/LibHac/FsSrv/Storage/EmulatedStorageDeviceManagerFactory.cs @@ -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 manger = GameCardManager.CreateShared(_fsServer); - _gameCardDeviceManager.SetByMove(ref manger.Ref); + using SharedRef manager = GameCardManager.CreateShared(_gc, _fsServer); + _gameCardDeviceManager.SetByMove(ref manager.Ref); } else { - _dummyGameCardDeviceManager.Reset(new DummyGameCardManager()); + using SharedRef manager = DummyGameCardManager.CreateShared(); + _dummyGameCardDeviceManager.SetByMove(ref manager.Ref); } } } diff --git a/src/LibHac/FsSystem/Utility.cs b/src/LibHac/FsSystem/Utility.cs index 020eafe0..e977b883 100644 --- a/src/LibHac/FsSystem/Utility.cs +++ b/src/LibHac/FsSystem/Utility.cs @@ -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(); + } +} + /// /// Various utility functions used by the namespace. /// diff --git a/src/LibHac/Gc/GameCardValues.cs b/src/LibHac/Gc/GameCardValues.cs index 2abcfd05..e25d869a 100644 --- a/src/LibHac/Gc/GameCardValues.cs +++ b/src/LibHac/Gc/GameCardValues.cs @@ -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; } \ No newline at end of file diff --git a/src/LibHac/GcSrv/DummyGameCardManager.cs b/src/LibHac/GcSrv/DummyGameCardManager.cs index 46d5e63a..163b8f33 100644 --- a/src/LibHac/GcSrv/DummyGameCardManager.cs +++ b/src/LibHac/GcSrv/DummyGameCardManager.cs @@ -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 _eventNotifier; + + // LibHac additions + private WeakRef _selfReference; + + private DummyGameCardManager() { - throw new NotImplementedException(); + _eventNotifier = new SharedRef(new DummyEventNotifier()); + } + + public static SharedRef CreateShared() + { + var manager = new DummyGameCardManager(); + + using var sharedManager = new SharedRef(manager); + manager._selfReference.Set(in sharedManager); + + return SharedRef.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 outDetectionEvent) { - throw new NotImplementedException(); + outDetectionEvent.SetByCopy(in _eventNotifier); + return Result.Success; } public Result OpenOperator(ref SharedRef outDeviceOperator) { - throw new NotImplementedException(); + using SharedRef deviceOperator = SharedRef.Create(in _selfReference); + + if (!deviceOperator.HasValue) + return ResultFs.AllocationMemoryFailedInGameCardManagerG.Log(); + + outDeviceOperator.SetByMove(ref deviceOperator.Ref); + + return Result.Success; } public Result OpenDevice(ref SharedRef outStorageDevice, ulong attribute) @@ -157,7 +182,7 @@ public class DummyGameCardManager : IStorageDeviceManager, IStorageDeviceOperato { case GameCardManagerOperationIdValue.IsGameCardActivationValid: { - if (outBuffer.Size < Unsafe.SizeOf()) + if (outBuffer.Size < sizeof(bool)) return ResultFs.InvalidArgument.Log(); outBuffer.As() = false; @@ -186,4 +211,9 @@ public class DummyGameCardManager : IStorageDeviceManager, IStorageDeviceOperato return ResultFs.NotImplemented.Log(); } + + public void PresetInternalKeys(ReadOnlySpan gameCardKey, ReadOnlySpan gameCardCertificate) + { + // Empty + } } \ No newline at end of file diff --git a/src/LibHac/GcSrv/GameCardDeviceDetectionEventManager.cs b/src/LibHac/GcSrv/GameCardDetectionEventManager.cs similarity index 62% rename from src/LibHac/GcSrv/GameCardDeviceDetectionEventManager.cs rename to src/LibHac/GcSrv/GameCardDetectionEventManager.cs index 48cddc3d..5cd2dac0 100644 --- a/src/LibHac/GcSrv/GameCardDeviceDetectionEventManager.cs +++ b/src/LibHac/GcSrv/GameCardDetectionEventManager.cs @@ -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(); } diff --git a/src/LibHac/GcSrv/GameCardDeviceOperator.cs b/src/LibHac/GcSrv/GameCardDeviceOperator.cs new file mode 100644 index 00000000..1088f72c --- /dev/null +++ b/src/LibHac/GcSrv/GameCardDeviceOperator.cs @@ -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 _storageDevice; + + // LibHac additions + private readonly GameCardDummy _gc; + + public static uint BytesToPages(long byteCount) + { + return (uint)((ulong)byteCount / (ulong)GcPageSize); + } + + public GameCardDeviceOperator(ref SharedRef storageDevice, GameCardDummy gc) + { + _storageDevice = SharedRef.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(); + 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())); + + 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()) + { + switch (operation) + { + case GameCardOperationIdValue.GetGameCardIdSet: + { + Result res = _storageDevice.Get.AcquireReadLock(ref readLock.Ref()); + if (res.IsFailure()) return res.Miss(); + + if (buffer.Size < Unsafe.SizeOf()) + return ResultFs.InvalidArgument.Log(); + + result = _gc.GetGameCardIdSet(out buffer.As()); + bytesWritten = Unsafe.SizeOf(); + + 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()) + return ResultFs.InvalidArgument.Log(); + + result = _gc.GetCardStatus(out buffer.As()); + bytesWritten = Unsafe.SizeOf(); + + 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(); + 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(); + } +} \ No newline at end of file diff --git a/src/LibHac/GcSrv/GameCardManager.cs b/src/LibHac/GcSrv/GameCardManager.cs index 60afb754..7c190ded 100644 --- a/src/LibHac/GcSrv/GameCardManager.cs +++ b/src/LibHac/GcSrv/GameCardManager.cs @@ -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 _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 CreateShared(FileSystemServer fsServer) + public static SharedRef CreateShared(GameCardDummy gc, FileSystemServer fsServer) { - var manager = new GameCardManager(fsServer); + var manager = new GameCardManager(gc, fsServer); using var sharedManager = new SharedRef(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(_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(_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(_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(_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()); return Result.Success; - + } case GameCardManagerOperationIdValue.EraseAndWriteParamDirectly: + { if (buffer.Size < Unsafe.SizeOf()) 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()) return ResultFs.InvalidArgument.Log(); @@ -490,8 +510,9 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG bytesWritten = Unsafe.SizeOf(); return Result.Success; - + } case GameCardManagerOperationIdValue.GetGameCardErrorReportInfo: + { if (buffer.Size < Unsafe.SizeOf()) return ResultFs.InvalidArgument.Log(); @@ -500,8 +521,9 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG bytesWritten = Unsafe.SizeOf(); return Result.Success; - + } case GameCardManagerOperationIdValue.ReadParamDirectly: + { if (buffer.Size < Unsafe.SizeOf()) return ResultFs.InvalidArgument.Log(); @@ -510,6 +532,7 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG bytesWritten = Unsafe.SizeOf(); 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()) @@ -561,20 +586,22 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG bytesWritten = Unsafe.SizeOf(); 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 outBuffer, ReadOnlySpan 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(_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 tmpBuffer = stackalloc byte[writeSize]; - tmpBuffer.Clear(); + Span 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 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 buffer) { - Result res; + Result result; using (new SharedLock(_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(_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 outLock, GameCardHandle handle) { - using var readLock = new SharedLock(_rwLock); - - if (_state != CardState.Initial && !_gc.IsCardActivationValid()) + using (var readLock = new SharedLock(_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 outLock, ref GameCardHandle handle, + public Result AcquireSecureLock(ref SharedLock outLock, ref GameCardHandle inOutHandle, ReadOnlySpan cardDeviceId, ReadOnlySpan cardImageHash) { using (var readLock = new SharedLock(_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 currentCardDeviceId = stackalloc byte[Values.GcCardDeviceIdSize]; - Span currentCardImageHash = stackalloc byte[Values.GcCardImageHashSize]; + Span currentCardDeviceId = stackalloc byte[GcCardDeviceIdSize]; + Span 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(); diff --git a/src/LibHac/GcSrv/GameCardStorage.cs b/src/LibHac/GcSrv/GameCardStorage.cs index d67b4bfa..c2da5657 100644 --- a/src/LibHac/GcSrv/GameCardStorage.cs +++ b/src/LibHac/GcSrv/GameCardStorage.cs @@ -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 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 _baseStorage; + + protected GameCardStorageInterfaceAdapter(ref SharedRef baseStorage) + { + _baseStorage = SharedRef.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.Empty).Ret(); + } } \ No newline at end of file diff --git a/src/LibHac/GcSrv/GameCardStorageDevice.cs b/src/LibHac/GcSrv/GameCardStorageDevice.cs index be34bea5..8bc036d8 100644 --- a/src/LibHac/GcSrv/GameCardStorageDevice.cs +++ b/src/LibHac/GcSrv/GameCardStorageDevice.cs @@ -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 _baseStorage; - - protected GameCardStorageInterfaceAdapter(ref SharedRef baseStorage) - { - _baseStorage = SharedRef.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.Empty).Ret(); - } -} - internal class GameCardStorageDevice : GameCardStorageInterfaceAdapter, IStorageDevice { private SharedRef _manager; @@ -67,16 +18,23 @@ internal class GameCardStorageDevice : GameCardStorageInterfaceAdapter, IStorage private Array16 _cardDeviceId; private Array32 _cardImageHash; - public GameCardStorageDevice(ref SharedRef manager, ref SharedRef baseStorage, - GameCardHandle handle) : base(ref baseStorage) + // LibHac additions + private WeakRef _selfReference; + private readonly GameCardDummy _gc; + + private GameCardStorageDevice(GameCardDummy gc, ref SharedRef manager, + ref SharedRef baseStorage, GameCardHandle handle) : base(ref baseStorage) { _manager = SharedRef.CreateMove(ref manager); _handle = handle; _isSecure = false; + + _gc = gc; } - public GameCardStorageDevice(ref SharedRef manager, ref SharedRef baseStorage, - GameCardHandle handle, bool isSecure, ReadOnlySpan cardDeviceId, ReadOnlySpan cardImageHash) + private GameCardStorageDevice(GameCardDummy gc, ref SharedRef manager, + ref SharedRef baseStorage, GameCardHandle handle, bool isSecure, ReadOnlySpan cardDeviceId, + ReadOnlySpan 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 CreateShared(GameCardDummy gc, + ref SharedRef manager, ref SharedRef baseStorage, GameCardHandle handle) + { + var storageDevice = new GameCardStorageDevice(gc, ref manager, ref baseStorage, handle); + + using var sharedStorageDevice = new SharedRef(storageDevice); + storageDevice._selfReference.Set(in sharedStorageDevice); + + return SharedRef.CreateMove(ref sharedStorageDevice.Ref); + } + + public static SharedRef CreateShared(GameCardDummy gc, + ref SharedRef manager, ref SharedRef baseStorage, GameCardHandle handle, + bool isSecure, ReadOnlySpan cardDeviceId, ReadOnlySpan cardImageHash) + { + var storageDevice = new GameCardStorageDevice(gc, ref manager, ref baseStorage, handle, isSecure, cardDeviceId, + cardImageHash); + + using var sharedStorageDevice = new SharedRef(storageDevice); + storageDevice._selfReference.Set(in sharedStorageDevice); + + return SharedRef.CreateMove(ref sharedStorageDevice.Ref); } public override void Dispose() { _manager.Destroy(); + _selfReference.Destroy(); base.Dispose(); } public Result AcquireReadLock(ref SharedLock 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(); + 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 outDeviceOperator) { - throw new NotImplementedException(); + using var readLock = new SharedLock(); + + Result res = AcquireReadLock(ref readLock.Ref()); + if (res.IsFailure()) return res.Miss(); + + using SharedRef storageDevice = + SharedRef.Create(in _selfReference); + + using var deviceOperator = + new SharedRef(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()) - { - Result res = AcquireWriteLock(ref writeLock.Ref()); - if (res.IsFailure()) return res.Miss(); - - return HandleGameCardAccessResult(resultGetSize).Ret(); - } + return HandleGameCardAccessResult(resultGetSize).Ret(); } } \ No newline at end of file diff --git a/src/LibHac/GcSrv/IGameCardManager.cs b/src/LibHac/GcSrv/IGameCardManager.cs index 472edc6c..513fa602 100644 --- a/src/LibHac/GcSrv/IGameCardManager.cs +++ b/src/LibHac/GcSrv/IGameCardManager.cs @@ -6,7 +6,7 @@ namespace LibHac.GcSrv; internal interface IGameCardManager : IDisposable { Result AcquireReadLock(ref SharedLock outLock, GameCardHandle handle); - Result AcquireSecureLock(ref SharedLock outLock, ref GameCardHandle handle, ReadOnlySpan cardDeviceId, ReadOnlySpan cardImageHash); + Result AcquireSecureLock(ref SharedLock outLock, ref GameCardHandle inOutHandle, ReadOnlySpan cardDeviceId, ReadOnlySpan cardImageHash); Result AcquireWriteLock(ref UniqueLock outLock); Result HandleGameCardAccessResult(Result result); Result GetHandle(out GameCardHandle outHandle);