From 902e3e14248e57fd8c90ec13d9198d59e23deaae Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 4 May 2022 13:09:06 -0700 Subject: [PATCH] Implement DeviceOperator --- src/LibHac/Fs/FsEnums.cs | 38 +- src/LibHac/Fs/Shim/GameCard.cs | 11 +- src/LibHac/FsSrv/EmulatedDeviceOperator.cs | 225 ++++++- src/LibHac/FsSrv/FileSystemServer.cs | 1 + src/LibHac/FsSrv/Impl/DeviceOperator.cs | 606 ++++++++++++++++++ src/LibHac/FsSrv/Sf/IDeviceOperator.cs | 47 +- .../FsSystem/SpeedEmulationConfiguration.cs | 26 + src/LibHac/SdmmcSrv/Common.cs | 14 + src/LibHac/Util/IntUtil.cs | 5 + 9 files changed, 942 insertions(+), 31 deletions(-) create mode 100644 src/LibHac/FsSrv/Impl/DeviceOperator.cs create mode 100644 src/LibHac/FsSystem/SpeedEmulationConfiguration.cs create mode 100644 src/LibHac/SdmmcSrv/Common.cs diff --git a/src/LibHac/Fs/FsEnums.cs b/src/LibHac/Fs/FsEnums.cs index ceaaa0ed..814d9618 100644 --- a/src/LibHac/Fs/FsEnums.cs +++ b/src/LibHac/Fs/FsEnums.cs @@ -194,28 +194,30 @@ public enum SdCardSpeedMode public enum SdmmcBusWidth { - Width1Bit = 0, - Width4Bit = 1, - Width8Bit = 2, + Unknown = 0, + Width1Bit = 1, + Width4Bit = 2, + Width8Bit = 3, } public enum SdmmcSpeedMode { - MmcIdentification = 0, - MmcLegacySpeed = 1, - MmcHighSpeed = 2, - MmcHs200 = 3, - MmcHs400 = 4, - SdCardIdentification = 5, - SdCardDefaultSpeed = 6, - SdCardHighSpeed = 7, - SdCardSdr12 = 8, - SdCardSdr25 = 9, - SdCardSdr50 = 10, - SdCardSdr104 = 11, - SdCardDdr50 = 12, - GcAsicFpgaSpeed = 13, - GcAsicSpeed = 14 + Unknown = 0, + MmcIdentification = 1, + MmcLegacySpeed = 2, + MmcHighSpeed = 3, + MmcHs200 = 4, + MmcHs400 = 5, + SdCardIdentification = 6, + SdCardDefaultSpeed = 7, + SdCardHighSpeed = 8, + SdCardSdr12 = 9, + SdCardSdr25 = 10, + SdCardSdr50 = 11, + SdCardSdr104 = 12, + SdCardDdr50 = 13, + GcAsicFpgaSpeed = 14, + GcAsicSpeed = 15 } public enum SdmmcPort diff --git a/src/LibHac/Fs/Shim/GameCard.cs b/src/LibHac/Fs/Shim/GameCard.cs index 0dab8227..f51f98b4 100644 --- a/src/LibHac/Fs/Shim/GameCard.cs +++ b/src/LibHac/Fs/Shim/GameCard.cs @@ -72,9 +72,9 @@ public static class GameCard } } - public static Result GetGameCardHandle(this FileSystemClient fs, out GameCardHandle handle) + public static Result GetGameCardHandle(this FileSystemClient fs, out GameCardHandle outHandle) { - UnsafeHelpers.SkipParamInit(out handle); + UnsafeHelpers.SkipParamInit(out outHandle); using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); using var deviceOperator = new SharedRef(); @@ -83,9 +83,12 @@ public static class GameCard fs.Impl.AbortIfNeeded(rc); if (rc.IsFailure()) return rc; - rc = deviceOperator.Get.GetGameCardHandle(out handle); + rc = deviceOperator.Get.GetGameCardHandle(out uint handle); fs.Impl.AbortIfNeeded(rc); - return rc; + if (rc.IsFailure()) return rc.Miss(); + + outHandle = new GameCardHandle((int)handle); + return Result.Success; } public static Result MountGameCardPartition(this FileSystemClient fs, U8Span mountName, GameCardHandle handle, diff --git a/src/LibHac/FsSrv/EmulatedDeviceOperator.cs b/src/LibHac/FsSrv/EmulatedDeviceOperator.cs index e1fe7e5f..d6e6452c 100644 --- a/src/LibHac/FsSrv/EmulatedDeviceOperator.cs +++ b/src/LibHac/FsSrv/EmulatedDeviceOperator.cs @@ -1,6 +1,8 @@ -using LibHac.Common; +using System; +using LibHac.Common; using LibHac.Fs; using LibHac.FsSrv.Sf; +using LibHac.Sf; namespace LibHac.FsSrv; @@ -17,18 +19,229 @@ public class EmulatedDeviceOperator : IDeviceOperator public void Dispose() { } - public Result IsSdCardInserted(out bool isInserted) + public Result IsSdCardInserted(out bool outIsInserted) { - isInserted = SdCard.IsSdCardInserted(); + outIsInserted = SdCard.IsSdCardInserted(); return Result.Success; } - public Result IsGameCardInserted(out bool isInserted) + public Result GetSdCardSpeedMode(out long outSpeedMode) { - isInserted = GameCard.IsGameCardInserted(); + throw new NotImplementedException(); + } + + public Result GetSdCardCid(OutBuffer outBuffer, long outBufferSize) + { + throw new NotImplementedException(); + } + + public Result GetSdCardUserAreaSize(out long outSize) + { + throw new NotImplementedException(); + } + + public Result GetSdCardProtectedAreaSize(out long outSize) + { + throw new NotImplementedException(); + } + + public Result GetAndClearSdCardErrorInfo(out StorageErrorInfo outStorageErrorInfo, out long outLogSize, + OutBuffer logBuffer, long logBufferSize) + { + throw new NotImplementedException(); + } + + public Result GetMmcCid(OutBuffer outBuffer, long outBufferSize) + { + throw new NotImplementedException(); + } + + public Result GetMmcSpeedMode(out long outSpeedMode) + { + throw new NotImplementedException(); + } + + public Result EraseMmc(uint partitionId) + { + throw new NotImplementedException(); + } + + public Result GetMmcPartitionSize(out long outSize, uint partitionId) + { + throw new NotImplementedException(); + } + + public Result GetMmcPatrolCount(out uint outCount) + { + throw new NotImplementedException(); + } + + public Result GetAndClearMmcErrorInfo(out StorageErrorInfo outStorageErrorInfo, out long outLogSize, + OutBuffer logBuffer, long logBufferSize) + { + throw new NotImplementedException(); + } + + public Result GetMmcExtendedCsd(OutBuffer outBuffer, long outBufferSize) + { + throw new NotImplementedException(); + } + + public Result SuspendMmcPatrol() + { + throw new NotImplementedException(); + } + + public Result ResumeMmcPatrol() + { + throw new NotImplementedException(); + } + + public Result IsGameCardInserted(out bool outIsInserted) + { + outIsInserted = GameCard.IsGameCardInserted(); return Result.Success; } + public Result EraseGameCard(uint gameCardSize, ulong romAreaStartPageAddress) + { + throw new NotImplementedException(); + } + + public Result GetGameCardHandle(out uint outHandle) + { + throw new NotImplementedException(); + } + + public Result GetGameCardUpdatePartitionInfo(out uint outCupVersion, out ulong outCupId, uint handle) + { + throw new NotImplementedException(); + } + + public Result FinalizeGameCardDriver() + { + throw new NotImplementedException(); + } + + public Result GetGameCardAttribute(out byte outAttribute, uint handle) + { + throw new NotImplementedException(); + } + + public Result GetGameCardDeviceCertificate(OutBuffer outBuffer, long outBufferSize, uint handle) + { + throw new NotImplementedException(); + } + + public Result GetGameCardAsicInfo(OutBuffer outRmaInfoBuffer, long rmaInfoBufferSize, InBuffer asicFirmwareBuffer, + long asicFirmwareBufferSize) + { + throw new NotImplementedException(); + } + + public Result GetGameCardIdSet(OutBuffer outBuffer, long outBufferSize) + { + throw new NotImplementedException(); + } + + public Result WriteToGameCardDirectly(long offset, OutBuffer buffer, long bufferSize) + { + throw new NotImplementedException(); + } + + public Result SetVerifyWriteEnableFlag(bool isEnabled) + { + throw new NotImplementedException(); + } + + public Result GetGameCardImageHash(OutBuffer outBuffer, long outBufferSize, uint handle) + { + throw new NotImplementedException(); + } + + public Result GetGameCardDeviceIdForProdCard(OutBuffer outBuffer, long outBufferSize, InBuffer devHeaderBuffer, + long devHeaderBufferSize) + { + throw new NotImplementedException(); + } + + public Result EraseAndWriteParamDirectly(InBuffer inBuffer, long inBufferSize) + { + throw new NotImplementedException(); + } + + public Result ReadParamDirectly(OutBuffer outBuffer, long outBufferSize) + { + throw new NotImplementedException(); + } + + public Result ForceEraseGameCard() + { + throw new NotImplementedException(); + } + + public Result GetGameCardErrorInfo(out GameCardErrorInfo outErrorInfo) + { + throw new NotImplementedException(); + } + + public Result GetGameCardErrorReportInfo(out GameCardErrorReportInfo outErrorInfo) + { + throw new NotImplementedException(); + } + + public Result GetGameCardDeviceId(OutBuffer outBuffer, long outBufferSize) + { + throw new NotImplementedException(); + } + + public Result ChallengeCardExistence(OutBuffer outResponseBuffer, InBuffer challengeSeedBuffer, + InBuffer challengeValueBuffer, uint handle) + { + throw new NotImplementedException(); + } + + public Result GetGameCardCompatibilityType(out byte outCompatibilityType, uint handle) + { + throw new NotImplementedException(); + } + + public Result SetSpeedEmulationMode(int mode) + { + throw new NotImplementedException(); + } + + public Result GetSpeedEmulationMode(out int outMode) + { + throw new NotImplementedException(); + } + + public Result SuspendSdmmcControl() + { + throw new NotImplementedException(); + } + + public Result ResumeSdmmcControl() + { + throw new NotImplementedException(); + } + + public Result GetSdmmcConnectionStatus(out int outSpeedMode, out int outBusWidth, int port) + { + throw new NotImplementedException(); + } + + public Result SetDeviceSimulationEvent(uint port, uint simulatedOperationType, uint simulatedFailureType, + uint failureResult, bool autoClearEvent) + { + throw new NotImplementedException(); + } + + public Result ClearDeviceSimulationEvent(uint port) + { + throw new NotImplementedException(); + } + public Result GetGameCardHandle(out GameCardHandle handle) { UnsafeHelpers.SkipParamInit(out handle); @@ -39,4 +252,4 @@ public class EmulatedDeviceOperator : IDeviceOperator handle = GameCard.GetGameCardHandle(); return Result.Success; } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/FileSystemServer.cs b/src/LibHac/FsSrv/FileSystemServer.cs index 9667c075..2d6d368a 100644 --- a/src/LibHac/FsSrv/FileSystemServer.cs +++ b/src/LibHac/FsSrv/FileSystemServer.cs @@ -45,6 +45,7 @@ internal struct FileSystemServerGlobals : IDisposable public PooledBufferGlobals PooledBuffer; public GameCardServiceGlobals GameCardService; public HierarchicalIntegrityVerificationStorageGlobals HierarchicalIntegrityVerificationStorage; + public SpeedEmulationConfigurationGlobals SpeedEmulationConfiguration; public void Initialize(HorizonClient horizonClient, FileSystemServer fsServer) { diff --git a/src/LibHac/FsSrv/Impl/DeviceOperator.cs b/src/LibHac/FsSrv/Impl/DeviceOperator.cs new file mode 100644 index 00000000..ad05ebb9 --- /dev/null +++ b/src/LibHac/FsSrv/Impl/DeviceOperator.cs @@ -0,0 +1,606 @@ +using System; +using System.Runtime.CompilerServices; +using LibHac.Common; +using LibHac.Diag; +using LibHac.Fs; +using LibHac.FsSrv.Sf; +using LibHac.FsSrv.Storage; +using LibHac.FsSystem; +using LibHac.Gc; +using LibHac.Sdmmc; +using LibHac.Sf; +using LibHac.Util; + +namespace LibHac.FsSrv.Impl; + +/// +/// Verifies permissions for calls and forwards them +/// to the appropriate functions. +/// +/// Based on nnSdk 14.3.0 +public class DeviceOperator : IDeviceOperator +{ + private AccessControl _accessControl; + // ReSharper disable once NotAccessedField.Local + private ulong _processId; + + // LibHac addition + private FileSystemServer _fsServer; + + public DeviceOperator(FileSystemServer fsServer, AccessControl accessControl, ulong processId) + { + _accessControl = accessControl; + _processId = processId; + + _fsServer = fsServer; + } + + public void Dispose() { } + + private static Span GetSpan(OutBuffer buffer, long size) + { + Assert.True(IntUtil.IsIntValueRepresentableAsInt(size)); + + return buffer.Buffer.Slice(0, (int)size); + } + + private static ReadOnlySpan GetSpan(InBuffer buffer, long size) + { + Assert.True(IntUtil.IsIntValueRepresentableAsInt(size)); + + return buffer.Buffer.Slice(0, (int)size); + } + + public Result IsSdCardInserted(out bool outIsInserted) + { + return _fsServer.Storage.IsSdCardInserted(out outIsInserted).Ret(); + } + + public Result GetSdCardCid(OutBuffer outBuffer, long outBufferSize) + { + if (outBuffer.Size < outBufferSize) + return ResultFs.InvalidSize.Log(); + + return _fsServer.Storage.GetSdCardCid(GetSpan(outBuffer, outBufferSize)).Ret(); + } + + public Result GetSdCardSpeedMode(out long outSpeedMode) + { + UnsafeHelpers.SkipParamInit(out outSpeedMode); + + Result rc = _fsServer.Storage.GetSdCardSpeedMode(out SdCardSpeedMode speedMode); + if (rc.IsFailure()) return rc.Miss(); + + outSpeedMode = (long)speedMode; + return Result.Success; + } + + public Result GetSdCardUserAreaSize(out long outSize) + { + UnsafeHelpers.SkipParamInit(out outSize); + + Result rc = _fsServer.Storage.GetSdCardUserAreaSize(out long size); + if (rc.IsFailure()) return rc.Miss(); + + outSize = size; + return Result.Success; + } + + public Result GetSdCardProtectedAreaSize(out long outSize) + { + UnsafeHelpers.SkipParamInit(out outSize); + + Result rc = _fsServer.Storage.GetSdCardProtectedAreaSize(out long size); + if (rc.IsFailure()) return rc.Miss(); + + outSize = size; + return Result.Success; + } + + public Result GetAndClearSdCardErrorInfo(out StorageErrorInfo outStorageErrorInfo, out long outLogSize, + OutBuffer logBuffer, long logBufferSize) + { + UnsafeHelpers.SkipParamInit(out outStorageErrorInfo, out outLogSize); + + if (logBuffer.Size < logBufferSize) + return ResultFs.InvalidSize.Log(); + + Result rc = _fsServer.Storage.GetAndClearSdCardErrorInfo(out StorageErrorInfo storageErrorInfo, + out long logSize, GetSpan(logBuffer, logBufferSize)); + if (rc.IsFailure()) return rc.Miss(); + + outStorageErrorInfo = storageErrorInfo; + outLogSize = logSize; + return Result.Success; + } + + public Result GetMmcCid(OutBuffer outBuffer, long outBufferSize) + { + if (outBuffer.Size < outBufferSize) + return ResultFs.InvalidSize.Log(); + + return _fsServer.Storage.GetMmcCid(GetSpan(outBuffer, outBufferSize)); + } + + public Result GetMmcSpeedMode(out long outSpeedMode) + { + UnsafeHelpers.SkipParamInit(out outSpeedMode); + + Result rc = _fsServer.Storage.GetMmcSpeedMode(out MmcSpeedMode speedMode); + if (rc.IsFailure()) return rc.Miss(); + + outSpeedMode = (long)speedMode; + return Result.Success; + } + + public Result EraseMmc(uint partitionId) + { + if (!_accessControl.CanCall(OperationType.EraseMmc)) + return ResultFs.PermissionDenied.Log(); + + return _fsServer.Storage.EraseMmc((MmcPartition)partitionId).Ret(); + } + + public Result GetMmcPartitionSize(out long outSize, uint partitionId) + { + UnsafeHelpers.SkipParamInit(out outSize); + + Result rc = _fsServer.Storage.GetMmcPartitionSize(out long mmcPartitionSize, (MmcPartition)partitionId); + if (rc.IsFailure()) return rc.Miss(); + + outSize = mmcPartitionSize; + return Result.Success; + } + + public Result GetMmcPatrolCount(out uint outCount) + { + UnsafeHelpers.SkipParamInit(out outCount); + + Result rc = _fsServer.Storage.GetMmcPatrolCount(out uint mmcPatrolCount); + if (rc.IsFailure()) return rc.Miss(); + + outCount = mmcPatrolCount; + return Result.Success; + } + + public Result GetAndClearMmcErrorInfo(out StorageErrorInfo outStorageErrorInfo, out long outLogSize, + OutBuffer logBuffer, long logBufferSize) + { + UnsafeHelpers.SkipParamInit(out outStorageErrorInfo, out outLogSize); + + if (logBuffer.Size < logBufferSize) + return ResultFs.InvalidSize.Log(); + + Result rc = _fsServer.Storage.GetAndClearMmcErrorInfo(out StorageErrorInfo storageErrorInfo, out long logSize, + GetSpan(logBuffer, logBufferSize)); + if (rc.IsFailure()) return rc.Miss(); + + outStorageErrorInfo = storageErrorInfo; + outLogSize = logSize; + return Result.Success; + } + + public Result GetMmcExtendedCsd(OutBuffer outBuffer, long outBufferSize) + { + if (outBuffer.Size < outBufferSize) + return ResultFs.InvalidSize.Log(); + + return _fsServer.Storage.GetMmcExtendedCsd(GetSpan(outBuffer, outBufferSize)).Ret(); + } + + public Result SuspendMmcPatrol() + { + if (!_accessControl.CanCall(OperationType.ControlMmcPatrol)) + return ResultFs.PermissionDenied.Log(); + + return _fsServer.Storage.SuspendMmcPatrol().Ret(); + } + + public Result ResumeMmcPatrol() + { + if (!_accessControl.CanCall(OperationType.ControlMmcPatrol)) + return ResultFs.PermissionDenied.Log(); + + return _fsServer.Storage.ResumeMmcPatrol().Ret(); + } + + public Result IsGameCardInserted(out bool outIsInserted) + { + UnsafeHelpers.SkipParamInit(out outIsInserted); + + Result rc = _fsServer.Storage.IsGameCardInserted(out bool isInserted); + if (rc.IsFailure()) return rc.Miss(); + + outIsInserted = isInserted; + return Result.Success; + } + + public Result EraseGameCard(uint gameCardSize, ulong romAreaStartPageAddress) + { + Accessibility accessibility = _accessControl.GetAccessibilityFor(AccessibilityType.OpenGameCardStorage); + + if (!accessibility.CanWrite) + return ResultFs.PermissionDenied.Log(); + + return _fsServer.Storage.EraseGameCard(gameCardSize, romAreaStartPageAddress).Ret(); + } + + public Result GetGameCardHandle(out uint outHandle) + { + UnsafeHelpers.SkipParamInit(out outHandle); + + Result rc = _fsServer.Storage.GetInitializationResult(); + if (rc.IsFailure()) return rc.Miss(); + + _fsServer.Storage.IsGameCardInserted(out bool isInserted).IgnoreResult(); + if (!isInserted) + return ResultFs.GameCardFsGetHandleFailure.Log(); + + rc = _fsServer.Storage.GetGameCardHandle(out uint handle); + if (rc.IsFailure()) return rc.Miss(); + + outHandle = handle; + return Result.Success; + } + + public Result GetGameCardUpdatePartitionInfo(out uint outCupVersion, out ulong outCupId, uint handle) + { + UnsafeHelpers.SkipParamInit(out outCupVersion, out outCupId); + + Result rc = _fsServer.Storage.GetGameCardStatus(out GameCardStatus gameCardStatus, handle); + if (rc.IsFailure()) return rc.Miss(); + + outCupVersion = gameCardStatus.UpdatePartitionVersion; + outCupId = gameCardStatus.UpdatePartitionId; + + return Result.Success; + } + + public Result FinalizeGameCardDriver() + { + if (!_accessControl.CanCall(OperationType.FinalizeGameCardDriver)) + return ResultFs.PermissionDenied.Log(); + + _fsServer.Storage.FinalizeGameCardLibrary().IgnoreResult(); + return Result.Success; + } + + public Result GetGameCardAttribute(out byte outAttribute, uint handle) + { + UnsafeHelpers.SkipParamInit(out outAttribute); + + Result rc = _fsServer.Storage.GetGameCardStatus(out GameCardStatus gameCardStatus, handle); + if (rc.IsFailure()) return rc.Miss(); + + outAttribute = gameCardStatus.GameCardAttribute; + return Result.Success; + } + + public Result GetGameCardCompatibilityType(out byte outCompatibilityType, uint handle) + { + UnsafeHelpers.SkipParamInit(out outCompatibilityType); + + Result rc = _fsServer.Storage.GetGameCardStatus(out GameCardStatus gameCardStatus, handle); + if (rc.IsFailure()) return rc.Miss(); + + outCompatibilityType = gameCardStatus.CompatibilityType; + return Result.Success; + } + + public Result GetGameCardDeviceCertificate(OutBuffer outBuffer, long outBufferSize, uint handle) + { + if (!_accessControl.CanCall(OperationType.GetGameCardDeviceCertificate)) + return ResultFs.PermissionDenied.Log(); + + if (outBuffer.Size < outBufferSize) + return ResultFs.InvalidSize.Log(); + + return _fsServer.Storage.GetGameCardDeviceCertificate(GetSpan(outBuffer, outBufferSize), handle).Ret(); + } + + public Result ChallengeCardExistence(OutBuffer outResponseBuffer, InBuffer challengeSeedBuffer, + InBuffer challengeValueBuffer, uint handle) + { + if (!_accessControl.CanCall(OperationType.ChallengeCardExistence)) + return ResultFs.PermissionDenied.Log(); + + return _fsServer.Storage.ChallengeCardExistence(outResponseBuffer.Buffer, challengeSeedBuffer.Buffer, + challengeValueBuffer.Buffer, handle).Ret(); + } + + public Result GetGameCardAsicInfo(OutBuffer outRmaInfoBuffer, long rmaInfoBufferSize, InBuffer asicFirmwareBuffer, + long asicFirmwareBufferSize) + { + if (!_accessControl.CanCall(OperationType.GetGameCardAsicInfo)) + return ResultFs.PermissionDenied.Log(); + + if (outRmaInfoBuffer.Size < rmaInfoBufferSize) + return ResultFs.InvalidSize.Log(); + + if (asicFirmwareBuffer.Size < asicFirmwareBufferSize) + return ResultFs.InvalidSize.Log(); + + if (rmaInfoBufferSize != Unsafe.SizeOf()) + return ResultFs.InvalidArgument.Log(); + + Result rc = _fsServer.Storage.GetGameCardAsicInfo(out RmaInformation rmaInfo, + GetSpan(asicFirmwareBuffer, asicFirmwareBufferSize)); + if (rc.IsFailure()) return rc.Miss(); + + SpanHelpers.AsReadOnlyByteSpan(in rmaInfo).CopyTo(outRmaInfoBuffer.Buffer); + return Result.Success; + } + + public Result GetGameCardIdSet(OutBuffer outBuffer, long outBufferSize) + { + if (outBuffer.Size < outBufferSize) + return ResultFs.InvalidSize.Log(); + + if (outBufferSize != Unsafe.SizeOf()) + return ResultFs.InvalidArgument.Log(); + + Result rc = _fsServer.Storage.GetGameCardIdSet(out GameCardIdSet gcIdSet); + if (rc.IsFailure()) return rc.Miss(); + + SpanHelpers.AsReadOnlyByteSpan(in gcIdSet).CopyTo(outBuffer.Buffer); + return Result.Success; + } + + public Result WriteToGameCardDirectly(long offset, OutBuffer buffer, long bufferSize) + { + Accessibility accessibility = _accessControl.GetAccessibilityFor(AccessibilityType.OpenGameCardStorage); + + if (!accessibility.CanWrite) + return ResultFs.PermissionDenied.Log(); + + if (buffer.Size < bufferSize) + return ResultFs.InvalidSize.Log(); + + // Changed: Removed the alignment check for the buffer address + + if (!Alignment.IsAlignedPow2(bufferSize, 0x1000)) + return ResultFs.InvalidAlignment.Log(); + + return _fsServer.Storage.WriteToGameCardDirectly(offset, GetSpan(buffer, bufferSize)).Ret(); + } + + public Result SetVerifyWriteEnableFlag(bool isEnabled) + { + _fsServer.Storage.SetVerifyWriteEnableFlag(isEnabled); + return Result.Success; + } + + public Result GetGameCardImageHash(OutBuffer outBuffer, long outBufferSize, uint handle) + { + if (outBuffer.Size < outBufferSize) + return ResultFs.InvalidSize.Log(); + + return _fsServer.Storage.GetGameCardImageHash(GetSpan(outBuffer, outBufferSize), handle).Ret(); + } + + public Result GetGameCardDeviceIdForProdCard(OutBuffer outBuffer, long outBufferSize, InBuffer devHeaderBuffer, + long devHeaderBufferSize) + { + Accessibility accessibility = _accessControl.GetAccessibilityFor(AccessibilityType.OpenGameCardStorage); + + if (!accessibility.CanWrite) + return ResultFs.PermissionDenied.Log(); + + if (outBuffer.Size < outBufferSize) + return ResultFs.InvalidSize.Log(); + + if (devHeaderBuffer.Size < devHeaderBufferSize) + return ResultFs.InvalidSize.Log(); + + return _fsServer.Storage.GetGameCardDeviceIdForProdCard(GetSpan(outBuffer, outBufferSize), + GetSpan(devHeaderBuffer, devHeaderBufferSize)).Ret(); + } + + public Result EraseAndWriteParamDirectly(InBuffer inBuffer, long inBufferSize) + { + Accessibility accessibility = _accessControl.GetAccessibilityFor(AccessibilityType.OpenGameCardStorage); + + if (!accessibility.CanWrite) + return ResultFs.PermissionDenied.Log(); + + if (inBuffer.Size < inBufferSize) + return ResultFs.InvalidSize.Log(); + + return _fsServer.Storage.EraseAndWriteParamDirectly(GetSpan(inBuffer, inBufferSize)).Ret(); + } + + public Result ReadParamDirectly(OutBuffer outBuffer, long outBufferSize) + { + Accessibility accessibility = _accessControl.GetAccessibilityFor(AccessibilityType.OpenGameCardStorage); + + if (!accessibility.CanWrite) + return ResultFs.PermissionDenied.Log(); + + if (outBuffer.Size < outBufferSize) + return ResultFs.InvalidSize.Log(); + + return _fsServer.Storage.ReadParamDirectly(GetSpan(outBuffer, outBufferSize)).Ret(); + } + + public Result ForceEraseGameCard() + { + Accessibility accessibility = _accessControl.GetAccessibilityFor(AccessibilityType.OpenGameCardStorage); + + if (!accessibility.CanWrite) + return ResultFs.PermissionDenied.Log(); + + return _fsServer.Storage.ForceEraseGameCard().Ret(); + } + + public Result GetGameCardErrorInfo(out GameCardErrorInfo outErrorInfo) + { + UnsafeHelpers.SkipParamInit(out outErrorInfo); + + Result rc = _fsServer.Storage.GetGameCardErrorInfo(out GameCardErrorInfo gameCardErrorInfo); + if (rc.IsFailure()) return rc.Miss(); + + outErrorInfo = gameCardErrorInfo; + return Result.Success; + } + + public Result GetGameCardErrorReportInfo(out GameCardErrorReportInfo outErrorInfo) + { + UnsafeHelpers.SkipParamInit(out outErrorInfo); + + Result rc = _fsServer.Storage.GetGameCardErrorReportInfo(out GameCardErrorReportInfo gameCardErrorReportInfo); + if (rc.IsFailure()) return rc.Miss(); + + outErrorInfo = gameCardErrorReportInfo; + return Result.Success; + } + + public Result GetGameCardDeviceId(OutBuffer outBuffer, long outBufferSize) + { + if (outBuffer.Size < outBufferSize) + return ResultFs.InvalidSize.Log(); + + return _fsServer.Storage.GetGameCardDeviceId(GetSpan(outBuffer, outBufferSize)).Ret(); + } + + public Result SetSpeedEmulationMode(int mode) + { + if (!_accessControl.CanCall(OperationType.SetSpeedEmulationMode)) + return ResultFs.PermissionDenied.Log(); + + _fsServer.SetSpeedEmulationMode((SpeedEmulationMode)mode); + return Result.Success; + } + + public Result GetSpeedEmulationMode(out int outMode) + { + outMode = (int)_fsServer.GetSpeedEmulationMode(); + return Result.Success; + } + + public Result SuspendSdmmcControl() + { + Result rc = _fsServer.Storage.SuspendSdCardControl(); + if (rc.IsFailure()) return rc.Miss(); + + // Missing: Detach SD card device buffer + + rc = _fsServer.Storage.SuspendMmcControl(); + if (rc.IsFailure()) return rc.Miss(); + + // Missing: Detach MMC device buffer + + return Result.Success; + } + + public Result ResumeSdmmcControl() + { + // Missing: Attach MMC device buffer + + Result rc = _fsServer.Storage.ResumeMmcControl(); + if (rc.IsFailure()) return rc.Miss(); + + // Missing: Attach SD card device buffer + + rc = _fsServer.Storage.ResumeSdCardControl(); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public Result GetSdmmcConnectionStatus(out int outSpeedMode, out int outBusWidth, int port) + { + UnsafeHelpers.SkipParamInit(out outSpeedMode, out outBusWidth); + + if ((uint)port > (uint)Port.GcAsic0) + return ResultFs.InvalidArgument.Log(); + + Result rc = SdmmcSrv.Common.CheckConnection(out SpeedMode speedMode, out BusWidth busWidth, (Port)port); + if (rc.IsFailure()) return rc.Miss(); + + SdmmcSpeedMode sdmmcSpeedMode = speedMode switch + { + SpeedMode.MmcIdentification => SdmmcSpeedMode.MmcIdentification, + SpeedMode.MmcLegacySpeed => SdmmcSpeedMode.MmcLegacySpeed, + SpeedMode.MmcHighSpeed => SdmmcSpeedMode.MmcHighSpeed, + SpeedMode.MmcHs200 => SdmmcSpeedMode.MmcHs200, + SpeedMode.MmcHs400 => SdmmcSpeedMode.MmcHs400, + SpeedMode.SdCardIdentification => SdmmcSpeedMode.SdCardIdentification, + SpeedMode.SdCardDefaultSpeed => SdmmcSpeedMode.SdCardDefaultSpeed, + SpeedMode.SdCardHighSpeed => SdmmcSpeedMode.SdCardHighSpeed, + SpeedMode.SdCardSdr12 => SdmmcSpeedMode.SdCardSdr12, + SpeedMode.SdCardSdr25 => SdmmcSpeedMode.SdCardSdr25, + SpeedMode.SdCardSdr50 => SdmmcSpeedMode.SdCardSdr50, + SpeedMode.SdCardSdr104 => SdmmcSpeedMode.SdCardSdr104, + SpeedMode.SdCardDdr50 => SdmmcSpeedMode.SdCardDdr50, + SpeedMode.GcAsicFpgaSpeed => SdmmcSpeedMode.GcAsicFpgaSpeed, + SpeedMode.GcAsicSpeed => SdmmcSpeedMode.GcAsicSpeed, + _ => SdmmcSpeedMode.Unknown + }; + + SdmmcBusWidth sdmmcBusWidth = busWidth switch + { + BusWidth.Width1Bit => SdmmcBusWidth.Width1Bit, + BusWidth.Width4Bit => SdmmcBusWidth.Width4Bit, + BusWidth.Width8Bit => SdmmcBusWidth.Width8Bit, + _ => SdmmcBusWidth.Unknown + }; + + outSpeedMode = (int)sdmmcSpeedMode; + outBusWidth = (int)sdmmcBusWidth; + + return Result.Success; + } + + public Result SetDeviceSimulationEvent(uint port, uint simulatedOperationType, uint simulatedFailureType, + uint failureResult, bool autoClearEvent) + { + if (!_accessControl.CanCall(OperationType.SimulateDevice)) + return ResultFs.PermissionDenied.Log(); + + var respondingFailureResult = new Result(failureResult); + + switch ((Port)port) + { + case Port.GcAsic0: + _fsServer.Impl.GetGameCardEventSimulator().SetDeviceEvent( + (SimulatingDeviceTargetOperation)simulatedOperationType, + (SimulatingDeviceAccessFailureEventType)simulatedFailureType, respondingFailureResult, + autoClearEvent); + break; + case Port.SdCard0: + _fsServer.Impl.GetSdCardEventSimulator().SetDeviceEvent( + (SimulatingDeviceTargetOperation)simulatedOperationType, + (SimulatingDeviceAccessFailureEventType)simulatedFailureType, respondingFailureResult, + autoClearEvent); + break; + case Port.Mmc0: + return ResultFs.NotImplemented.Log(); + default: + return ResultFs.StorageDeviceInvalidOperation.Log(); + } + + return Result.Success; + } + + public Result ClearDeviceSimulationEvent(uint port) + { + if (!_accessControl.CanCall(OperationType.SimulateDevice)) + return ResultFs.PermissionDenied.Log(); + + switch ((Port)port) + { + case Port.GcAsic0: + _fsServer.Impl.GetGameCardEventSimulator().ClearDeviceEvent(); + break; + case Port.SdCard0: + _fsServer.Impl.GetSdCardEventSimulator().ClearDeviceEvent(); + break; + case Port.Mmc0: + return ResultFs.NotImplemented.Log(); + default: + return ResultFs.StorageDeviceInvalidOperation.Log(); + } + + return Result.Success; + } +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/Sf/IDeviceOperator.cs b/src/LibHac/FsSrv/Sf/IDeviceOperator.cs index 13164242..df0dfc1e 100644 --- a/src/LibHac/FsSrv/Sf/IDeviceOperator.cs +++ b/src/LibHac/FsSrv/Sf/IDeviceOperator.cs @@ -1,11 +1,52 @@ using System; using LibHac.Fs; +using LibHac.Sf; namespace LibHac.FsSrv.Sf; public interface IDeviceOperator : IDisposable { - Result IsSdCardInserted(out bool isInserted); - Result IsGameCardInserted(out bool isInserted); - Result GetGameCardHandle(out GameCardHandle handle); + Result IsSdCardInserted(out bool outIsInserted); + Result GetSdCardSpeedMode(out long outSpeedMode); + Result GetSdCardCid(OutBuffer outBuffer, long outBufferSize); + Result GetSdCardUserAreaSize(out long outSize); + Result GetSdCardProtectedAreaSize(out long outSize); + Result GetAndClearSdCardErrorInfo(out StorageErrorInfo outStorageErrorInfo, out long outLogSize, OutBuffer logBuffer, long logBufferSize); + Result GetMmcCid(OutBuffer outBuffer, long outBufferSize); + Result GetMmcSpeedMode(out long outSpeedMode); + Result EraseMmc(uint partitionId); + Result GetMmcPartitionSize(out long outSize, uint partitionId); + Result GetMmcPatrolCount(out uint outCount); + Result GetAndClearMmcErrorInfo(out StorageErrorInfo outStorageErrorInfo, out long outLogSize, OutBuffer logBuffer, long logBufferSize); + Result GetMmcExtendedCsd(OutBuffer outBuffer, long outBufferSize); + Result SuspendMmcPatrol(); + Result ResumeMmcPatrol(); + Result IsGameCardInserted(out bool outIsInserted); + Result EraseGameCard(uint gameCardSize, ulong romAreaStartPageAddress); + Result GetGameCardHandle(out uint outHandle); + Result GetGameCardUpdatePartitionInfo(out uint outCupVersion, out ulong outCupId, uint handle); + Result FinalizeGameCardDriver(); + Result GetGameCardAttribute(out byte outAttribute, uint handle); + Result GetGameCardDeviceCertificate(OutBuffer outBuffer, long outBufferSize, uint handle); + Result GetGameCardAsicInfo(OutBuffer outRmaInfoBuffer, long rmaInfoBufferSize, InBuffer asicFirmwareBuffer, long asicFirmwareBufferSize); + Result GetGameCardIdSet(OutBuffer outBuffer, long outBufferSize); + Result WriteToGameCardDirectly(long offset, OutBuffer buffer, long bufferSize); + Result SetVerifyWriteEnableFlag(bool isEnabled); + Result GetGameCardImageHash(OutBuffer outBuffer, long outBufferSize, uint handle); + Result GetGameCardDeviceIdForProdCard(OutBuffer outBuffer, long outBufferSize, InBuffer devHeaderBuffer, long devHeaderBufferSize); + Result EraseAndWriteParamDirectly(InBuffer inBuffer, long inBufferSize); + Result ReadParamDirectly(OutBuffer outBuffer, long outBufferSize); + Result ForceEraseGameCard(); + Result GetGameCardErrorInfo(out GameCardErrorInfo outErrorInfo); + Result GetGameCardErrorReportInfo(out GameCardErrorReportInfo outErrorInfo); + Result GetGameCardDeviceId(OutBuffer outBuffer, long outBufferSize); + Result ChallengeCardExistence(OutBuffer outResponseBuffer, InBuffer challengeSeedBuffer, InBuffer challengeValueBuffer, uint handle); + Result GetGameCardCompatibilityType(out byte outCompatibilityType, uint handle); + Result SetSpeedEmulationMode(int mode); + Result GetSpeedEmulationMode(out int outMode); + Result SuspendSdmmcControl(); + Result ResumeSdmmcControl(); + Result GetSdmmcConnectionStatus(out int outSpeedMode, out int outBusWidth, int port); + Result SetDeviceSimulationEvent(uint port, uint simulatedOperationType, uint simulatedFailureType, uint failureResult, bool autoClearEvent); + Result ClearDeviceSimulationEvent(uint port); } \ No newline at end of file diff --git a/src/LibHac/FsSystem/SpeedEmulationConfiguration.cs b/src/LibHac/FsSystem/SpeedEmulationConfiguration.cs new file mode 100644 index 00000000..2895ed5c --- /dev/null +++ b/src/LibHac/FsSystem/SpeedEmulationConfiguration.cs @@ -0,0 +1,26 @@ +using LibHac.Fs; +using LibHac.FsSrv; + +namespace LibHac.FsSystem; + +internal struct SpeedEmulationConfigurationGlobals +{ + public SpeedEmulationMode SpeedEmulationMode; +} + +/// +/// Handles getting and setting the configuration for storage speed emulation. +/// +/// Based on nnSdk 14.3.0 +internal static class SpeedEmulationConfiguration +{ + public static void SetSpeedEmulationMode(this FileSystemServer fsServer, SpeedEmulationMode mode) + { + fsServer.Globals.SpeedEmulationConfiguration.SpeedEmulationMode = mode; + } + + public static SpeedEmulationMode GetSpeedEmulationMode(this FileSystemServer fsServer) + { + return fsServer.Globals.SpeedEmulationConfiguration.SpeedEmulationMode; + } +} \ No newline at end of file diff --git a/src/LibHac/SdmmcSrv/Common.cs b/src/LibHac/SdmmcSrv/Common.cs new file mode 100644 index 00000000..2569400e --- /dev/null +++ b/src/LibHac/SdmmcSrv/Common.cs @@ -0,0 +1,14 @@ +using LibHac.Sdmmc; + +namespace LibHac.SdmmcSrv; + +public static class Common +{ + public static Result CheckConnection(out SpeedMode outSpeedMode, out BusWidth outBusWidth, Port port) + { + outSpeedMode = default; + outBusWidth = default; + + return Result.Success; + } +} \ No newline at end of file diff --git a/src/LibHac/Util/IntUtil.cs b/src/LibHac/Util/IntUtil.cs index c0582580..52524e31 100644 --- a/src/LibHac/Util/IntUtil.cs +++ b/src/LibHac/Util/IntUtil.cs @@ -13,6 +13,11 @@ public static class IntUtil return value >= 0; } + public static bool IsIntValueRepresentableAsInt(long value) + { + return value >= int.MinValue && value <= int.MaxValue; + } + public static bool IsIntValueRepresentableAsUInt(long value) { return value >= uint.MinValue && value <= uint.MaxValue;