From dc6f4fa4892784e96179afaf017ecbcc450263d1 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 16 Jul 2024 20:47:31 -0700 Subject: [PATCH] Implement MemoryStorageCreator and NspRootFileSystemCreator --- src/LibHac/Fs/MemoryStorage.cs | 88 +++++++++++++++++++ .../FsSrv/FsCreator/IMemoryStorageCreator.cs | 14 ++- .../FsCreator/INspRootFileSystemCreator.cs | 4 + .../FsSrv/FsCreator/MemoryStorageCreator.cs | 78 ++++++++++++++++ .../FsCreator/NspRootFileSystemCreator.cs | 23 +++++ 5 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 src/LibHac/FsSrv/FsCreator/MemoryStorageCreator.cs create mode 100644 src/LibHac/FsSrv/FsCreator/NspRootFileSystemCreator.cs diff --git a/src/LibHac/Fs/MemoryStorage.cs b/src/LibHac/Fs/MemoryStorage.cs index 0df7b325..d321df7b 100644 --- a/src/LibHac/Fs/MemoryStorage.cs +++ b/src/LibHac/Fs/MemoryStorage.cs @@ -75,6 +75,94 @@ public class MemoryStorage : IStorage return Result.Success; } + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + switch (operationId) + { + case OperationId.InvalidateCache: + return Result.Success; + case OperationId.QueryRange: + if (outBuffer.Length != Unsafe.SizeOf()) + return ResultFs.InvalidSize.Log(); + + Unsafe.As(ref MemoryMarshal.GetReference(outBuffer)).Clear(); + return Result.Success; + default: + return ResultFs.UnsupportedOperateRangeForMemoryStorage.Log(); + } + } +} + +/// +/// Allows interacting with a via the interface. +/// +/// Based on nnSdk 16.2.0 (FS 16.0.0) +internal class MemoryStorageFromMemory : IStorage +{ + private Memory _buffer; + private int _size; + + public MemoryStorageFromMemory(Memory buffer) + { + _buffer = buffer; + _size = buffer.Length; + } + + public MemoryStorageFromMemory(Memory buffer, int size) + { + Assert.SdkRequiresInRange(size, 0, buffer.Length); + + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + Abort.DoAbortUnless(0 <= size && size < buffer.Length); + + _buffer = buffer; + _size = size; + } + + public override Result Read(long offset, Span destination) + { + if (destination.Length == 0) + return Result.Success; + + Result res = CheckAccessRange(offset, destination.Length, _size); + if (res.IsFailure()) return res.Miss(); + + _buffer.Span.Slice((int)offset, destination.Length).CopyTo(destination); + + return Result.Success; + } + + public override Result Write(long offset, ReadOnlySpan source) + { + if (source.Length == 0) + return Result.Success; + + Result res = CheckAccessRange(offset, source.Length, _size); + if (res.IsFailure()) return res.Miss(); + + source.CopyTo(_buffer.Span.Slice((int)offset)); + + return Result.Success; + } + + public override Result Flush() + { + return Result.Success; + } + + public override Result SetSize(long size) + { + return ResultFs.UnsupportedSetSizeForMemoryStorage.Log(); + } + + public override Result GetSize(out long size) + { + size = _size; + + return Result.Success; + } + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { diff --git a/src/LibHac/FsSrv/FsCreator/IMemoryStorageCreator.cs b/src/LibHac/FsSrv/FsCreator/IMemoryStorageCreator.cs index 18ce63af..e349f67c 100644 --- a/src/LibHac/FsSrv/FsCreator/IMemoryStorageCreator.cs +++ b/src/LibHac/FsSrv/FsCreator/IMemoryStorageCreator.cs @@ -1,10 +1,20 @@ using System; +using LibHac.Common; using LibHac.Fs; namespace LibHac.FsSrv.FsCreator; public interface IMemoryStorageCreator { - Result Create(out IStorage storage, out Memory buffer, int storageId); - Result RegisterBuffer(int storageId, Memory buffer); + public enum MemoryStorageId + { + UserPartitionFatFs, + SignedSystemPartitionRaw, + SystemPartitionFatFs, + Id4, + Count + } + + Result Create(ref SharedRef outStorage, out Memory outBuffer, MemoryStorageId id); + Result RegisterBuffer(MemoryStorageId id, Memory buffer); } \ No newline at end of file diff --git a/src/LibHac/FsSrv/FsCreator/INspRootFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/INspRootFileSystemCreator.cs index 01d93cea..ffeb01cd 100644 --- a/src/LibHac/FsSrv/FsCreator/INspRootFileSystemCreator.cs +++ b/src/LibHac/FsSrv/FsCreator/INspRootFileSystemCreator.cs @@ -4,6 +4,10 @@ using LibHac.Fs.Fsa; namespace LibHac.FsSrv.FsCreator; +/// +/// Creates an from an containing a partition filesystem of version 0 or version 1. +/// +/// Based on nnSdk 18.3.0 (FS 18.0.0) public interface INspRootFileSystemCreator { Result Create(ref SharedRef outFileSystem, ref readonly SharedRef baseStorage); diff --git a/src/LibHac/FsSrv/FsCreator/MemoryStorageCreator.cs b/src/LibHac/FsSrv/FsCreator/MemoryStorageCreator.cs new file mode 100644 index 00000000..1f42c151 --- /dev/null +++ b/src/LibHac/FsSrv/FsCreator/MemoryStorageCreator.cs @@ -0,0 +1,78 @@ +using System; +using LibHac.Common; +using LibHac.Common.FixedArrays; +using LibHac.Diag; +using LibHac.Fs; +using LibHac.Os; + +namespace LibHac.FsSrv.FsCreator; + +/// +/// Creates s from registered memory buffers. +/// +/// +/// Used for in-memory System and User partitions when booting in safe mode. +/// On startup, FS registers buffers which can be used later if needed. +/// Based on nnSdk 18.3.0 (FS 18.0.0) +/// +public class MemoryStorageCreator : IMemoryStorageCreator +{ + private SdkMutexType _mutex; + private Array4 _bufferArray; + + private struct Buffer + { + public Memory MemoryBuffer; + public bool IsInUse; // Each registered buffer can only be used to create a MemoryStorage a single time + + public Buffer() + { + MemoryBuffer = default; + IsInUse = false; + } + } + + public MemoryStorageCreator() + { + _mutex = new SdkMutexType(); + + for (int i = 0; i < _bufferArray.Length; i++) + { + _bufferArray[i] = new Buffer(); + } + } + + public Result Create(ref SharedRef outStorage, out Memory outBuffer, IMemoryStorageCreator.MemoryStorageId id) + { + UnsafeHelpers.SkipParamInit(out outBuffer); + + using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex); + + ref Buffer buffer = ref _bufferArray[(int)id]; + + if (buffer.IsInUse) + return ResultFs.AllocationMemoryFailed.Log(); + + if (buffer.MemoryBuffer.IsEmpty) + return ResultFs.AllocationMemoryFailed.Log(); + + using var storage = new SharedRef(new MemoryStorageFromMemory(buffer.MemoryBuffer)); + buffer.MemoryBuffer.Span.Clear(); + + outStorage.SetByMove(ref storage.Ref); + outBuffer = buffer.MemoryBuffer; + buffer.IsInUse = true; + + return Result.Success; + } + + public Result RegisterBuffer(IMemoryStorageCreator.MemoryStorageId id, Memory buffer) + { + Assert.SdkAssert(id < IMemoryStorageCreator.MemoryStorageId.Count); + Assert.SdkAssert(_bufferArray[(int)id].MemoryBuffer.IsEmpty); + + _bufferArray[(int)id].MemoryBuffer = buffer; + + return Result.Success; + } +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/FsCreator/NspRootFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/NspRootFileSystemCreator.cs new file mode 100644 index 00000000..dd4f2359 --- /dev/null +++ b/src/LibHac/FsSrv/FsCreator/NspRootFileSystemCreator.cs @@ -0,0 +1,23 @@ +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.FsSystem; + +namespace LibHac.FsSrv.FsCreator; + +/// +public class NspRootFileSystemCreator : INspRootFileSystemCreator +{ + public Result Create(ref SharedRef outFileSystem, ref readonly SharedRef baseStorage) + { + using var nspFs = new SharedRef(new NintendoSubmissionPackageRootFileSystem()); + if (!nspFs.HasValue) + return ResultFs.AllocationMemoryFailedInPartitionFileSystemCreatorA.Log(); + + Result res = nspFs.Get.Initialize(in baseStorage); + if (res.IsFailure()) return res.Miss(); + + outFileSystem.SetByMove(ref nspFs.Ref); + return Result.Success; + } +} \ No newline at end of file