Add GameCardStorageDevice

This commit is contained in:
Alex Barney 2022-05-21 03:36:54 -07:00
parent 43d63086bf
commit f4c59771cb
7 changed files with 400 additions and 7 deletions

View file

@ -7,7 +7,7 @@ namespace LibHac.FsSrv.Storage.Sf;
// StorageServiceObjectAdapter is a template that is used with either IStorage or IStorageDevice
public interface IStorageDevice : IStorage
{
Result GetHandle(out uint handle);
Result GetHandle(out GameCardHandle handle);
Result IsHandleValid(out bool isValid);
Result OpenOperator(ref SharedRef<IStorageDeviceOperator> outDeviceOperator);

View file

@ -8,4 +8,5 @@ public static class Values
public static int GcCardExistenceResponseDataSize => 0x58;
public static int GcCardImageHashSize => 0x20;
public static int GcDeviceCertificateSize => 0x200;
public static int GcCardKeyAreaSize => 0x1000;
}

View file

@ -16,7 +16,7 @@ using IStorage = LibHac.FsSrv.Sf.IStorage;
namespace LibHac.GcSrv;
public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IGameCardDeviceManager
public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IGameCardManager, IGameCardKeyManager
{
private enum CardState
{
@ -64,7 +64,7 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
_rwLock = null;
}
private uint BytesToPages(long byteCount)
public static uint BytesToPages(long byteCount)
{
return (uint)((ulong)byteCount / (ulong)Values.GcPageSize);
}
@ -320,15 +320,50 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
public Result OpenOperator(ref SharedRef<IStorageDeviceOperator> outDeviceOperator)
{
throw new NotImplementedException();
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
using SharedRef<GameCardManager> deviceOperator = SharedRef<GameCardManager>.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)
{
throw new NotImplementedException();
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
using var storageDevice = new SharedRef<IStorageDevice>();
res = OpenDeviceImpl(ref storageDevice.Ref, (OpenGameCardAttribute)attribute);
if (res.IsFailure()) return res.Miss();
outStorageDevice.SetByMove(ref storageDevice.Ref);
return Result.Success;
}
public Result OpenStorage(ref SharedRef<IStorage> outStorage, ulong attribute)
{
Result res = InitializeGcLibrary();
if (res.IsFailure()) return res.Miss();
using var storageDevice = new SharedRef<IStorageDevice>();
res = OpenDeviceImpl(ref storageDevice.Ref, (OpenGameCardAttribute)attribute);
if (res.IsFailure()) return res.Miss();
outStorage.SetByMove(ref storageDevice.Ref);
return Result.Success;
}
private Result OpenDeviceImpl(ref SharedRef<IStorageDevice> outStorageDevice, OpenGameCardAttribute attribute)
{
throw new NotImplementedException();
}
@ -894,4 +929,9 @@ public class GameCardManager : IStorageDeviceManager, IStorageDeviceOperator, IG
{
return _state == CardState.Secure;
}
public void PresetInternalKeys(ReadOnlySpan<byte> gameCardKey, ReadOnlySpan<byte> gameCardCertificate)
{
throw new NotImplementedException();
}
}

View file

@ -0,0 +1,156 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.Gc;
using static LibHac.Gc.Values;
namespace LibHac.GcSrv;
internal class ReadOnlyGameCardStorage : IStorage
{
private SharedRef<IGameCardManager> _deviceManager;
// LibHac additions
private readonly GameCardDummy _gc;
public ReadOnlyGameCardStorage(ref SharedRef<IGameCardManager> deviceManger, GameCardDummy gc)
{
_deviceManager = SharedRef<IGameCardManager>.CreateMove(ref deviceManger);
_gc = gc;
}
public override void Dispose()
{
_deviceManager.Destroy();
base.Dispose();
}
public override Result Read(long offset, Span<byte> destination)
{
Assert.SdkRequiresAligned(offset, GcPageSize);
Assert.SdkRequiresAligned(destination.Length, GcPageSize);
if (destination.Length == 0)
return Result.Success;
// Missing: Allocate a device buffer if the destination buffer is not one
return _gc.Read(destination, GameCardManager.BytesToPages(offset),
GameCardManager.BytesToPages(destination.Length)).Ret();
}
public override Result Write(long offset, ReadOnlySpan<byte> source)
{
return ResultFs.UnsupportedWriteForReadOnlyGameCardStorage.Log();
}
public override Result Flush()
{
return Result.Success;
}
public override Result GetSize(out long size)
{
UnsafeHelpers.SkipParamInit(out size);
Result res = _gc.GetCardStatus(out GameCardStatus status);
if (res.IsFailure()) return res.Miss();
size = status.Size;
return Result.Success;
}
public override Result SetSize(long size)
{
return ResultFs.UnsupportedSetSizeForReadOnlyGameCardStorage.Log();
}
public override Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan<byte> inBuffer)
{
switch (operationId)
{
case OperationId.InvalidateCache:
return Result.Success;
case OperationId.QueryRange:
if (outBuffer.Length != Unsafe.SizeOf<QueryRangeInfo>())
return ResultFs.InvalidSize.Log();
SpanHelpers.AsStruct<QueryRangeInfo>(outBuffer).Clear();
return Result.Success;
default:
return ResultFs.UnsupportedOperateRangeForReadOnlyGameCardStorage.Log();
}
}
}
internal class WriteOnlyGameCardStorage : IStorage
{
private SharedRef<IGameCardManager> _deviceManager;
// LibHac additions
private readonly GameCardDummy _gc;
public WriteOnlyGameCardStorage(ref SharedRef<IGameCardManager> deviceManger, GameCardDummy gc)
{
_deviceManager = SharedRef<IGameCardManager>.CreateMove(ref deviceManger);
_gc = gc;
}
public override void Dispose()
{
_deviceManager.Destroy();
base.Dispose();
}
public override Result Read(long offset, Span<byte> destination)
{
return ResultFs.UnsupportedReadForWriteOnlyGameCardStorage.Log();
}
public override Result Write(long offset, ReadOnlySpan<byte> source)
{
Assert.SdkRequiresAligned(offset, GcPageSize);
Assert.SdkRequiresAligned(source.Length, GcPageSize);
if (source.Length == 0)
return Result.Success;
// 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();
}
public override Result Flush()
{
return Result.Success;
}
public override Result GetSize(out long size)
{
UnsafeHelpers.SkipParamInit(out size);
Result res = _gc.Writer.GetCardAvailableRawSize(out long gameCardSize);
if (res.IsFailure()) return res.Miss();
size = gameCardSize + GcCardKeyAreaSize;
return Result.Success;
}
public override Result SetSize(long size)
{
return ResultFs.UnsupportedSetSizeForWriteOnlyGameCardStorage.Log();
}
public override Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan<byte> inBuffer)
{
return ResultFs.NotImplemented.Log();
}
}

View file

@ -0,0 +1,196 @@
using System;
using LibHac.Common;
using LibHac.Common.FixedArrays;
using LibHac.Diag;
using LibHac.Fs;
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;
private GameCardHandle _handle;
private bool _isSecure;
private Array16<byte> _cardDeviceId;
private Array32<byte> _cardImageHash;
public GameCardStorageDevice(ref SharedRef<IGameCardManager> manager, ref SharedRef<IStorage> baseStorage,
GameCardHandle handle) : base(ref baseStorage)
{
_manager = SharedRef<IGameCardManager>.CreateMove(ref manager);
_handle = handle;
_isSecure = false;
}
public GameCardStorageDevice(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);
Assert.SdkRequiresEqual(cardImageHash.Length, Values.GcCardImageHashSize);
_manager = SharedRef<IGameCardManager>.CreateMove(ref manager);
_handle = handle;
_isSecure = isSecure;
cardDeviceId.CopyTo(_cardDeviceId.Items);
cardImageHash.CopyTo(_cardImageHash.Items);
}
public override void Dispose()
{
_manager.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();
}
return Result.Success;
}
public Result AcquireWriteLock(ref UniqueLock<ReaderWriterLock> outLock)
{
return _manager.Get.AcquireWriteLock(ref outLock).Ret();
}
public Result HandleGameCardAccessResult(Result result)
{
return _manager.Get.HandleGameCardAccessResult(result);
}
public Result GetHandle(out GameCardHandle handle)
{
handle = _handle;
return Result.Success;
}
public Result IsHandleValid(out bool isValid)
{
using var readLock = new SharedLock<ReaderWriterLock>();
isValid = _manager.Get.AcquireReadLock(ref readLock.Ref(), _handle).IsSuccess();
return Result.Success;
}
public Result OpenOperator(ref SharedRef<IStorageDeviceOperator> outDeviceOperator)
{
throw new NotImplementedException();
}
public override Result Read(long offset, OutBuffer destination, long size)
{
using var readLock = new SharedLock<ReaderWriterLock>();
Result res = AcquireReadLock(ref readLock.Ref());
if (res.IsFailure()) return res.Miss();
res = base.Read(offset, destination, size);
if (res.IsFailure()) return res.Miss();
return Result.Success;
}
public override Result Write(long offset, InBuffer source, long size)
{
using var readLock = new SharedLock<ReaderWriterLock>();
Result res = AcquireReadLock(ref readLock.Ref());
if (res.IsFailure()) return res.Miss();
res = base.Write(offset, source, size);
if (res.IsFailure()) return res.Miss();
return Result.Success;
}
public override Result GetSize(out long size)
{
Result resultGetSize;
UnsafeHelpers.SkipParamInit(out size);
using (var readLock = new SharedLock<ReaderWriterLock>())
{
Result res = AcquireReadLock(ref readLock.Ref());
if (res.IsFailure()) return res.Miss();
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();
}
}
}

View file

@ -2,7 +2,7 @@
namespace LibHac.GcSrv;
public interface IGameCardKeyManager
public interface IGameCardKeyManager : IDisposable
{
void PresetInternalKeys(ReadOnlySpan<byte> gameCardKey, ReadOnlySpan<byte> gameCardCertificate);
}

View file

@ -3,7 +3,7 @@ using LibHac.Os;
namespace LibHac.GcSrv;
internal interface IGameCardDeviceManager
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);