diff options
-rw-r--r-- | ARMeilleure/Instructions/InstEmitMemoryHelper.cs | 2 | ||||
-rw-r--r-- | Ryujinx.Cpu/InvalidAccessHandler.cs | 9 | ||||
-rw-r--r-- | Ryujinx.Cpu/MemoryManager.cs | 112 | ||||
-rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs | 25 | ||||
-rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs | 9 | ||||
-rw-r--r-- | Ryujinx.Memory/InvalidMemoryRegionException.cs | 19 | ||||
-rw-r--r-- | Ryujinx.Memory/MemoryBlock.cs | 36 |
7 files changed, 140 insertions, 72 deletions
diff --git a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs index 18e27e5a..0c47be61 100644 --- a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs +++ b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs @@ -326,7 +326,7 @@ namespace ARMeilleure.Instructions } while (bit < context.Memory.AddressSpaceBits); - context.BranchIfTrue(lblSlowPath, context.ICompareLess(pte, Const(0L))); + context.BranchIfTrue(lblSlowPath, context.ICompareLessOrEqual(pte, Const(0L))); Operand pageOffset = context.BitwiseAnd(address, Const(address.Type, PageMask)); diff --git a/Ryujinx.Cpu/InvalidAccessHandler.cs b/Ryujinx.Cpu/InvalidAccessHandler.cs new file mode 100644 index 00000000..0d3d387d --- /dev/null +++ b/Ryujinx.Cpu/InvalidAccessHandler.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Cpu +{ + /// <summary> + /// Function that handles a invalid memory access from the emulated CPU. + /// </summary> + /// <param name="va">Virtual address of the invalid region that is being accessed</param> + /// <returns>True if the invalid access should be ignored, false otherwise</returns> + public delegate bool InvalidAccessHandler(ulong va); +} diff --git a/Ryujinx.Cpu/MemoryManager.cs b/Ryujinx.Cpu/MemoryManager.cs index 211a8c0d..31357508 100644 --- a/Ryujinx.Cpu/MemoryManager.cs +++ b/Ryujinx.Cpu/MemoryManager.cs @@ -18,6 +18,11 @@ namespace Ryujinx.Cpu private const int PteSize = 8; + private readonly InvalidAccessHandler _invalidAccessHandler; + + /// <summary> + /// Address space width in bits. + /// </summary> public int AddressSpaceBits { get; } private readonly ulong _addressSpaceSize; @@ -25,6 +30,9 @@ namespace Ryujinx.Cpu private readonly MemoryBlock _backingMemory; private readonly MemoryBlock _pageTable; + /// <summary> + /// Page table base pointer. + /// </summary> public IntPtr PageTablePointer => _pageTable.Pointer; /// <summary> @@ -32,8 +40,11 @@ namespace Ryujinx.Cpu /// </summary> /// <param name="backingMemory">Physical backing memory where virtual memory will be mapped to</param> /// <param name="addressSpaceSize">Size of the address space</param> - public MemoryManager(MemoryBlock backingMemory, ulong addressSpaceSize) + /// <param name="invalidAccessHandler">Optional function to handle invalid memory accesses</param> + public MemoryManager(MemoryBlock backingMemory, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler = null) { + _invalidAccessHandler = invalidAccessHandler; + ulong asSize = PageSize; int asBits = PageBits; @@ -92,6 +103,7 @@ namespace Ryujinx.Cpu /// <typeparam name="T">Type of the data being read</typeparam> /// <param name="va">Virtual address of the data in memory</param> /// <returns>The data</returns> + /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception> public T Read<T>(ulong va) where T : unmanaged { return MemoryMarshal.Cast<byte, T>(GetSpan(va, Unsafe.SizeOf<T>()))[0]; @@ -102,6 +114,7 @@ namespace Ryujinx.Cpu /// </summary> /// <param name="va">Virtual address of the data in memory</param> /// <param name="data">Span to store the data being read into</param> + /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception> public void Read(ulong va, Span<byte> data) { ReadImpl(va, data); @@ -113,6 +126,7 @@ namespace Ryujinx.Cpu /// <typeparam name="T">Type of the data being written</typeparam> /// <param name="va">Virtual address to write the data into</param> /// <param name="value">Data to be written</param> + /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception> public void Write<T>(ulong va, T value) where T : unmanaged { Write(va, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1))); @@ -123,6 +137,7 @@ namespace Ryujinx.Cpu /// </summary> /// <param name="va">Virtual address to write the data into</param> /// <param name="data">Data to be written</param> + /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception> public void Write(ulong va, ReadOnlySpan<byte> data) { if (data.Length == 0) @@ -130,34 +145,44 @@ namespace Ryujinx.Cpu return; } - MarkRegionAsModified(va, (ulong)data.Length); - - if (IsContiguous(va, data.Length)) + try { - data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length)); - } - else - { - int offset = 0, size; + MarkRegionAsModified(va, (ulong)data.Length); - if ((va & PageMask) != 0) + if (IsContiguousAndMapped(va, data.Length)) { - ulong pa = GetPhysicalAddressInternal(va); + data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length)); + } + else + { + int offset = 0, size; - size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); + if ((va & PageMask) != 0) + { + ulong pa = GetPhysicalAddressInternal(va); - data.Slice(0, size).CopyTo(_backingMemory.GetSpan(pa, size)); + size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); - offset += size; - } + data.Slice(0, size).CopyTo(_backingMemory.GetSpan(pa, size)); - for (; offset < data.Length; offset += size) - { - ulong pa = GetPhysicalAddressInternal(va + (ulong)offset); + offset += size; + } - size = Math.Min(data.Length - offset, PageSize); + for (; offset < data.Length; offset += size) + { + ulong pa = GetPhysicalAddressInternal(va + (ulong)offset); + + size = Math.Min(data.Length - offset, PageSize); - data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size)); + data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size)); + } + } + } + catch (InvalidMemoryRegionException) + { + if (_invalidAccessHandler == null || !_invalidAccessHandler(va)) + { + throw; } } } @@ -172,6 +197,7 @@ namespace Ryujinx.Cpu /// <param name="va">Virtual address of the data</param> /// <param name="size">Size of the data</param> /// <returns>A read-only span of the data</returns> + /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception> public ReadOnlySpan<byte> GetSpan(ulong va, int size) { if (size == 0) @@ -179,7 +205,7 @@ namespace Ryujinx.Cpu return ReadOnlySpan<byte>.Empty; } - if (IsContiguous(va, size)) + if (IsContiguousAndMapped(va, size)) { return _backingMemory.GetSpan(GetPhysicalAddressInternal(va), size); } @@ -204,6 +230,7 @@ namespace Ryujinx.Cpu /// <param name="va">Virtual address of the data</param> /// <param name="size">Size of the data</param> /// <returns>A writable region of memory containing the data</returns> + /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception> public WritableRegion GetWritableRegion(ulong va, int size) { if (size == 0) @@ -211,7 +238,7 @@ namespace Ryujinx.Cpu return new WritableRegion(null, va, Memory<byte>.Empty); } - if (IsContiguous(va, size)) + if (IsContiguousAndMapped(va, size)) { return new WritableRegion(null, va, _backingMemory.GetMemory(GetPhysicalAddressInternal(va), size)); } @@ -234,6 +261,7 @@ namespace Ryujinx.Cpu /// <typeparam name="T">Type of the data to get the reference</typeparam> /// <param name="va">Virtual address of the data</param> /// <returns>A reference to the data in memory</returns> + /// <exception cref="MemoryNotContiguousException">Throw if the specified memory region is not contiguous in physical memory</exception> public ref T GetRef<T>(ulong va) where T : unmanaged { if (!IsContiguous(va, Unsafe.SizeOf<T>())) @@ -257,6 +285,9 @@ namespace Ryujinx.Cpu } [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool IsContiguous(ulong va, int size) { if (!ValidateAddress(va)) @@ -295,26 +326,36 @@ namespace Ryujinx.Cpu return; } - int offset = 0, size; - - if ((va & PageMask) != 0) + try { - ulong pa = GetPhysicalAddressInternal(va); + int offset = 0, size; - size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); + if ((va & PageMask) != 0) + { + ulong pa = GetPhysicalAddressInternal(va); - _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(0, size)); + size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); - offset += size; - } + _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(0, size)); - for (; offset < data.Length; offset += size) - { - ulong pa = GetPhysicalAddressInternal(va + (ulong)offset); + offset += size; + } - size = Math.Min(data.Length - offset, PageSize); + for (; offset < data.Length; offset += size) + { + ulong pa = GetPhysicalAddressInternal(va + (ulong)offset); + + size = Math.Min(data.Length - offset, PageSize); - _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(offset, size)); + _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(offset, size)); + } + } + catch (InvalidMemoryRegionException) + { + if (_invalidAccessHandler == null || !_invalidAccessHandler(va)) + { + throw; + } } } @@ -416,6 +457,7 @@ namespace Ryujinx.Cpu /// </summary> /// <param name="va">Virtual address to check</param> /// <returns>True if the address is mapped, false otherwise</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsMapped(ulong va) { if (!ValidateAddress(va)) diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 0fa22837..5d004694 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -1,5 +1,6 @@ using ARMeilleure.State; using Ryujinx.Common; +using Ryujinx.Common.Logging; using Ryujinx.Cpu; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Kernel.Common; @@ -1071,18 +1072,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return result; } - public void StopAllThreads() - { - lock (_threadingLock) - { - foreach (KThread thread in _threads) - { - KernelContext.Scheduler.ExitThread(thread); - KernelContext.Scheduler.CoreManager.Set(thread.HostThread); - } - } - } - private void InitializeMemoryManager(AddressSpaceType addrSpaceType, MemoryRegion memRegion) { int addrSpaceBits = addrSpaceType switch @@ -1094,7 +1083,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _ => throw new ArgumentException(nameof(addrSpaceType)) }; - CpuMemory = new MemoryManager(KernelContext.Memory, 1UL << addrSpaceBits); + CpuMemory = new MemoryManager(KernelContext.Memory, 1UL << addrSpaceBits, InvalidAccessHandler); CpuContext = new CpuContext(CpuMemory); // TODO: This should eventually be removed. @@ -1104,13 +1093,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Process MemoryManager = new KMemoryManager(KernelContext, CpuMemory); } - public void PrintCurrentThreadStackTrace() + private bool InvalidAccessHandler(ulong va) { - KernelContext.Scheduler.GetCurrentThread().PrintGuestStackTrace(); + KernelContext.Scheduler.GetCurrentThreadOrNull()?.PrintGuestStackTrace(); + + Logger.PrintError(LogClass.Cpu, $"Invalid memory access at virtual address 0x{va:X16}."); + + return false; } private void UndefinedInstructionHandler(object sender, InstUndefinedEventArgs e) { + KernelContext.Scheduler.GetCurrentThreadOrNull()?.PrintGuestStackTrace(); + throw new UndefinedInstructionException(e.Address, e.OpCode); } diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs index e07477ae..84995513 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs @@ -199,6 +199,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading public KThread GetCurrentThread() { + return GetCurrentThreadOrNull() ?? GetDummyThread(); + } + + public KThread GetCurrentThreadOrNull() + { lock (CoreContexts) { for (int core = 0; core < CpuCoresCount; core++) @@ -210,9 +215,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } } - return GetDummyThread(); - - throw new InvalidOperationException("Current thread is not scheduled!"); + return null; } private KThread _dummyThread; diff --git a/Ryujinx.Memory/InvalidMemoryRegionException.cs b/Ryujinx.Memory/InvalidMemoryRegionException.cs new file mode 100644 index 00000000..9fc938c6 --- /dev/null +++ b/Ryujinx.Memory/InvalidMemoryRegionException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Ryujinx.Memory +{ + public class InvalidMemoryRegionException : Exception + { + public InvalidMemoryRegionException() : base("Attempted to access a invalid memory region.") + { + } + + public InvalidMemoryRegionException(string message) : base(message) + { + } + + public InvalidMemoryRegionException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/Ryujinx.Memory/MemoryBlock.cs b/Ryujinx.Memory/MemoryBlock.cs index 065e0713..198e275b 100644 --- a/Ryujinx.Memory/MemoryBlock.cs +++ b/Ryujinx.Memory/MemoryBlock.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Memory /// Initializes a new instance of the memory block class. /// </summary> /// <param name="size">Size of the memory block</param> - /// <param name="flags">Flags that control memory block memory allocation</param> + /// <param name="flags">Flags that controls memory block memory allocation</param> /// <exception cref="OutOfMemoryException">Throw when there's no enough memory to allocate the requested size</exception> /// <exception cref="PlatformNotSupportedException">Throw when the current platform is not supported</exception> public MemoryBlock(ulong size, MemoryAllocationFlags flags = MemoryAllocationFlags.None) @@ -50,7 +50,7 @@ namespace Ryujinx.Memory /// <param name="size">Size of the range to be committed</param> /// <returns>True if the operation was successful, false otherwise</returns> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> - /// <exception cref="ArgumentOutOfRangeException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> + /// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> public bool Commit(ulong offset, ulong size) { return MemoryManagement.Commit(GetPointerInternal(offset, size), size); @@ -63,7 +63,7 @@ namespace Ryujinx.Memory /// <param name="size">Size of the range to be reprotected</param> /// <param name="permission">New memory permissions</param> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> - /// <exception cref="ArgumentOutOfRangeException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> + /// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> /// <exception cref="MemoryProtectionException">Throw when <paramref name="permission"/> is invalid</exception> public void Reprotect(ulong offset, ulong size, MemoryPermission permission) { @@ -76,7 +76,7 @@ namespace Ryujinx.Memory /// <param name="offset">Starting offset of the range being read</param> /// <param name="data">Span where the bytes being read will be copied to</param> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> - /// <exception cref="ArgumentOutOfRangeException">Throw when the memory region specified for the the data is out of range</exception> + /// <exception cref="InvalidMemoryRegionException">Throw when the memory region specified for the the data is out of range</exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Read(ulong offset, Span<byte> data) { @@ -90,7 +90,7 @@ namespace Ryujinx.Memory /// <param name="offset">Offset where the data is located</param> /// <returns>Data at the specified address</returns> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> - /// <exception cref="ArgumentOutOfRangeException">Throw when the memory region specified for the the data is out of range</exception> + /// <exception cref="InvalidMemoryRegionException">Throw when the memory region specified for the the data is out of range</exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public T Read<T>(ulong offset) where T : unmanaged { @@ -103,7 +103,7 @@ namespace Ryujinx.Memory /// <param name="offset">Starting offset of the range being written</param> /// <param name="data">Span where the bytes being written will be copied from</param> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> - /// <exception cref="ArgumentOutOfRangeException">Throw when the memory region specified for the the data is out of range</exception> + /// <exception cref="InvalidMemoryRegionException">Throw when the memory region specified for the the data is out of range</exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(ulong offset, ReadOnlySpan<byte> data) { @@ -117,7 +117,7 @@ namespace Ryujinx.Memory /// <param name="offset">Offset to write the data into</param> /// <param name="data">Data to be written</param> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> - /// <exception cref="ArgumentOutOfRangeException">Throw when the memory region specified for the the data is out of range</exception> + /// <exception cref="InvalidMemoryRegionException">Throw when the memory region specified for the the data is out of range</exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write<T>(ulong offset, T data) where T : unmanaged { @@ -131,7 +131,7 @@ namespace Ryujinx.Memory /// <param name="srcOffset">Source offset to read the data from</param> /// <param name="size">Size of the copy in bytes</param> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> - /// <exception cref="ArgumentOutOfRangeException">Throw when <paramref name="srcOffset"/>, <paramref name="dstOffset"/> or <paramref name="size"/> is out of range</exception> + /// <exception cref="InvalidMemoryRegionException">Throw when <paramref name="srcOffset"/>, <paramref name="dstOffset"/> or <paramref name="size"/> is out of range</exception> public void Copy(ulong dstOffset, ulong srcOffset, ulong size) { const int MaxChunkSize = 1 << 30; @@ -150,7 +150,7 @@ namespace Ryujinx.Memory /// <param name="offset">Offset of the region to fill with zeros</param> /// <param name="size">Size in bytes of the region to fill</param> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> - /// <exception cref="ArgumentOutOfRangeException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> + /// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> public void ZeroFill(ulong offset, ulong size) { const int MaxChunkSize = 1 << 30; @@ -170,7 +170,7 @@ namespace Ryujinx.Memory /// <param name="offset">Offset of the memory region</param> /// <returns>A reference to the given memory region data</returns> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> - /// <exception cref="ArgumentOutOfRangeException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> + /// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe ref T GetRef<T>(ulong offset) where T : unmanaged { @@ -187,7 +187,7 @@ namespace Ryujinx.Memory if (endOffset > Size || endOffset < offset) { - ThrowArgumentOutOfRange(); + ThrowInvalidMemoryRegionException(); } return ref Unsafe.AsRef<T>((void*)PtrAddr(ptr, offset)); @@ -200,7 +200,7 @@ namespace Ryujinx.Memory /// <param name="size">Size in bytes of the region</param> /// <returns>The pointer to the memory region</returns> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> - /// <exception cref="ArgumentOutOfRangeException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> + /// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public IntPtr GetPointer(ulong offset, int size) => GetPointerInternal(offset, (ulong)size); @@ -218,20 +218,20 @@ namespace Ryujinx.Memory if (endOffset > Size || endOffset < offset) { - ThrowArgumentOutOfRange(); + ThrowInvalidMemoryRegionException(); } return PtrAddr(ptr, offset); } /// <summary> - /// Gets the <see cref="System.Span{T}"/> of a given memory block region. + /// Gets the <see cref="Span{T}"/> of a given memory block region. /// </summary> /// <param name="offset">Start offset of the memory region</param> /// <param name="size">Size in bytes of the region</param> /// <returns>Span of the memory region</returns> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> - /// <exception cref="ArgumentOutOfRangeException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> + /// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe Span<byte> GetSpan(ulong offset, int size) { @@ -239,13 +239,13 @@ namespace Ryujinx.Memory } /// <summary> - /// Gets the <see cref="System.Memory{T}"/> of a given memory block region. + /// Gets the <see cref="Memory{T}"/> of a given memory block region. /// </summary> /// <param name="offset">Start offset of the memory region</param> /// <param name="size">Size in bytes of the region</param> /// <returns>Memory of the memory region</returns> /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> - /// <exception cref="ArgumentOutOfRangeException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> + /// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe Memory<byte> GetMemory(ulong offset, int size) { @@ -285,6 +285,6 @@ namespace Ryujinx.Memory } private void ThrowObjectDisposed() => throw new ObjectDisposedException(nameof(MemoryBlock)); - private void ThrowArgumentOutOfRange() => throw new ArgumentOutOfRangeException(); + private void ThrowInvalidMemoryRegionException() => throw new InvalidMemoryRegionException(); } } |