1
0
Fork 0
mirror of https://github.com/Ryujinx/Ryujinx.git synced 2024-10-01 12:30:00 +02:00

Print guest stack trace on invalid memory access (#1407)

* Print guest stack trace on invalid memory access

* Improve XML docs
This commit is contained in:
gdkchan 2020-07-30 10:16:41 -03:00 committed by GitHub
parent 636542d817
commit 57bb0abda3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 143 additions and 75 deletions

View file

@ -326,7 +326,7 @@ namespace ARMeilleure.Instructions
} }
while (bit < context.Memory.AddressSpaceBits); 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)); Operand pageOffset = context.BitwiseAnd(address, Const(address.Type, PageMask));

View file

@ -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);
}

View file

@ -18,6 +18,11 @@ namespace Ryujinx.Cpu
private const int PteSize = 8; private const int PteSize = 8;
private readonly InvalidAccessHandler _invalidAccessHandler;
/// <summary>
/// Address space width in bits.
/// </summary>
public int AddressSpaceBits { get; } public int AddressSpaceBits { get; }
private readonly ulong _addressSpaceSize; private readonly ulong _addressSpaceSize;
@ -25,6 +30,9 @@ namespace Ryujinx.Cpu
private readonly MemoryBlock _backingMemory; private readonly MemoryBlock _backingMemory;
private readonly MemoryBlock _pageTable; private readonly MemoryBlock _pageTable;
/// <summary>
/// Page table base pointer.
/// </summary>
public IntPtr PageTablePointer => _pageTable.Pointer; public IntPtr PageTablePointer => _pageTable.Pointer;
/// <summary> /// <summary>
@ -32,8 +40,11 @@ namespace Ryujinx.Cpu
/// </summary> /// </summary>
/// <param name="backingMemory">Physical backing memory where virtual memory will be mapped to</param> /// <param name="backingMemory">Physical backing memory where virtual memory will be mapped to</param>
/// <param name="addressSpaceSize">Size of the address space</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; ulong asSize = PageSize;
int asBits = PageBits; int asBits = PageBits;
@ -92,6 +103,7 @@ namespace Ryujinx.Cpu
/// <typeparam name="T">Type of the data being read</typeparam> /// <typeparam name="T">Type of the data being read</typeparam>
/// <param name="va">Virtual address of the data in memory</param> /// <param name="va">Virtual address of the data in memory</param>
/// <returns>The data</returns> /// <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 public T Read<T>(ulong va) where T : unmanaged
{ {
return MemoryMarshal.Cast<byte, T>(GetSpan(va, Unsafe.SizeOf<T>()))[0]; return MemoryMarshal.Cast<byte, T>(GetSpan(va, Unsafe.SizeOf<T>()))[0];
@ -102,6 +114,7 @@ namespace Ryujinx.Cpu
/// </summary> /// </summary>
/// <param name="va">Virtual address of the data in memory</param> /// <param name="va">Virtual address of the data in memory</param>
/// <param name="data">Span to store the data being read into</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) public void Read(ulong va, Span<byte> data)
{ {
ReadImpl(va, data); ReadImpl(va, data);
@ -113,6 +126,7 @@ namespace Ryujinx.Cpu
/// <typeparam name="T">Type of the data being written</typeparam> /// <typeparam name="T">Type of the data being written</typeparam>
/// <param name="va">Virtual address to write the data into</param> /// <param name="va">Virtual address to write the data into</param>
/// <param name="value">Data to be written</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 public void Write<T>(ulong va, T value) where T : unmanaged
{ {
Write(va, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1))); Write(va, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1)));
@ -123,6 +137,7 @@ namespace Ryujinx.Cpu
/// </summary> /// </summary>
/// <param name="va">Virtual address to write the data into</param> /// <param name="va">Virtual address to write the data into</param>
/// <param name="data">Data to be written</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) public void Write(ulong va, ReadOnlySpan<byte> data)
{ {
if (data.Length == 0) if (data.Length == 0)
@ -130,34 +145,44 @@ namespace Ryujinx.Cpu
return; return;
} }
MarkRegionAsModified(va, (ulong)data.Length); try
if (IsContiguous(va, data.Length))
{ {
data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length)); MarkRegionAsModified(va, (ulong)data.Length);
}
else
{
int offset = 0, size;
if ((va & PageMask) != 0) if (IsContiguousAndMapped(va, data.Length))
{ {
ulong pa = GetPhysicalAddressInternal(va); data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length));
size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
data.Slice(0, size).CopyTo(_backingMemory.GetSpan(pa, size));
offset += size;
} }
else
for (; offset < data.Length; offset += size)
{ {
ulong pa = GetPhysicalAddressInternal(va + (ulong)offset); int offset = 0, size;
size = Math.Min(data.Length - offset, PageSize); if ((va & PageMask) != 0)
{
ulong pa = GetPhysicalAddressInternal(va);
data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size)); size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
data.Slice(0, size).CopyTo(_backingMemory.GetSpan(pa, size));
offset += size;
}
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));
}
}
}
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="va">Virtual address of the data</param>
/// <param name="size">Size of the data</param> /// <param name="size">Size of the data</param>
/// <returns>A read-only span of the data</returns> /// <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) public ReadOnlySpan<byte> GetSpan(ulong va, int size)
{ {
if (size == 0) if (size == 0)
@ -179,7 +205,7 @@ namespace Ryujinx.Cpu
return ReadOnlySpan<byte>.Empty; return ReadOnlySpan<byte>.Empty;
} }
if (IsContiguous(va, size)) if (IsContiguousAndMapped(va, size))
{ {
return _backingMemory.GetSpan(GetPhysicalAddressInternal(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="va">Virtual address of the data</param>
/// <param name="size">Size of the data</param> /// <param name="size">Size of the data</param>
/// <returns>A writable region of memory containing the data</returns> /// <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) public WritableRegion GetWritableRegion(ulong va, int size)
{ {
if (size == 0) if (size == 0)
@ -211,7 +238,7 @@ namespace Ryujinx.Cpu
return new WritableRegion(null, va, Memory<byte>.Empty); 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)); 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> /// <typeparam name="T">Type of the data to get the reference</typeparam>
/// <param name="va">Virtual address of the data</param> /// <param name="va">Virtual address of the data</param>
/// <returns>A reference to the data in memory</returns> /// <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 public ref T GetRef<T>(ulong va) where T : unmanaged
{ {
if (!IsContiguous(va, Unsafe.SizeOf<T>())) if (!IsContiguous(va, Unsafe.SizeOf<T>()))
@ -256,6 +284,9 @@ namespace Ryujinx.Cpu
return ref _backingMemory.GetRef<T>(GetPhysicalAddressInternal(va)); return ref _backingMemory.GetRef<T>(GetPhysicalAddressInternal(va));
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsContiguous(ulong va, int size) private bool IsContiguous(ulong va, int size)
{ {
@ -295,26 +326,36 @@ namespace Ryujinx.Cpu
return; return;
} }
int offset = 0, size; try
if ((va & PageMask) != 0)
{ {
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));
offset += size;
}
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));
}
} }
catch (InvalidMemoryRegionException)
for (; offset < data.Length; offset += size)
{ {
ulong pa = GetPhysicalAddressInternal(va + (ulong)offset); if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
{
size = Math.Min(data.Length - offset, PageSize); throw;
}
_backingMemory.GetSpan(pa, size).CopyTo(data.Slice(offset, size));
} }
} }
@ -416,6 +457,7 @@ namespace Ryujinx.Cpu
/// </summary> /// </summary>
/// <param name="va">Virtual address to check</param> /// <param name="va">Virtual address to check</param>
/// <returns>True if the address is mapped, false otherwise</returns> /// <returns>True if the address is mapped, false otherwise</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsMapped(ulong va) public bool IsMapped(ulong va)
{ {
if (!ValidateAddress(va)) if (!ValidateAddress(va))

View file

@ -1,5 +1,6 @@
using ARMeilleure.State; using ARMeilleure.State;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Common;
@ -1071,18 +1072,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return result; 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) private void InitializeMemoryManager(AddressSpaceType addrSpaceType, MemoryRegion memRegion)
{ {
int addrSpaceBits = addrSpaceType switch int addrSpaceBits = addrSpaceType switch
@ -1094,7 +1083,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
_ => throw new ArgumentException(nameof(addrSpaceType)) _ => throw new ArgumentException(nameof(addrSpaceType))
}; };
CpuMemory = new MemoryManager(KernelContext.Memory, 1UL << addrSpaceBits); CpuMemory = new MemoryManager(KernelContext.Memory, 1UL << addrSpaceBits, InvalidAccessHandler);
CpuContext = new CpuContext(CpuMemory); CpuContext = new CpuContext(CpuMemory);
// TODO: This should eventually be removed. // TODO: This should eventually be removed.
@ -1104,13 +1093,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
MemoryManager = new KMemoryManager(KernelContext, CpuMemory); 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) private void UndefinedInstructionHandler(object sender, InstUndefinedEventArgs e)
{ {
KernelContext.Scheduler.GetCurrentThreadOrNull()?.PrintGuestStackTrace();
throw new UndefinedInstructionException(e.Address, e.OpCode); throw new UndefinedInstructionException(e.Address, e.OpCode);
} }

View file

@ -198,6 +198,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
} }
public KThread GetCurrentThread() public KThread GetCurrentThread()
{
return GetCurrentThreadOrNull() ?? GetDummyThread();
}
public KThread GetCurrentThreadOrNull()
{ {
lock (CoreContexts) lock (CoreContexts)
{ {
@ -210,9 +215,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
} }
} }
return GetDummyThread(); return null;
throw new InvalidOperationException("Current thread is not scheduled!");
} }
private KThread _dummyThread; private KThread _dummyThread;

View file

@ -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)
{
}
}
}

View file

@ -25,7 +25,7 @@ namespace Ryujinx.Memory
/// Initializes a new instance of the memory block class. /// Initializes a new instance of the memory block class.
/// </summary> /// </summary>
/// <param name="size">Size of the memory block</param> /// <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="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> /// <exception cref="PlatformNotSupportedException">Throw when the current platform is not supported</exception>
public MemoryBlock(ulong size, MemoryAllocationFlags flags = MemoryAllocationFlags.None) 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> /// <param name="size">Size of the range to be committed</param>
/// <returns>True if the operation was successful, false otherwise</returns> /// <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="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) public bool Commit(ulong offset, ulong size)
{ {
return MemoryManagement.Commit(GetPointerInternal(offset, size), 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="size">Size of the range to be reprotected</param>
/// <param name="permission">New memory permissions</param> /// <param name="permission">New memory permissions</param>
/// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> /// <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> /// <exception cref="MemoryProtectionException">Throw when <paramref name="permission"/> is invalid</exception>
public void Reprotect(ulong offset, ulong size, MemoryPermission permission) 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="offset">Starting offset of the range being read</param>
/// <param name="data">Span where the bytes being read will be copied to</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="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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Read(ulong offset, Span<byte> data) 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> /// <param name="offset">Offset where the data is located</param>
/// <returns>Data at the specified address</returns> /// <returns>Data at the specified address</returns>
/// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> /// <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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Read<T>(ulong offset) where T : unmanaged 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="offset">Starting offset of the range being written</param>
/// <param name="data">Span where the bytes being written will be copied from</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="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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(ulong offset, ReadOnlySpan<byte> data) 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="offset">Offset to write the data into</param>
/// <param name="data">Data to be written</param> /// <param name="data">Data to be written</param>
/// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> /// <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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write<T>(ulong offset, T data) where T : unmanaged 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="srcOffset">Source offset to read the data from</param>
/// <param name="size">Size of the copy in bytes</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="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) public void Copy(ulong dstOffset, ulong srcOffset, ulong size)
{ {
const int MaxChunkSize = 1 << 30; 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="offset">Offset of the region to fill with zeros</param>
/// <param name="size">Size in bytes of the region to fill</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="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) public void ZeroFill(ulong offset, ulong size)
{ {
const int MaxChunkSize = 1 << 30; const int MaxChunkSize = 1 << 30;
@ -170,7 +170,7 @@ namespace Ryujinx.Memory
/// <param name="offset">Offset of the memory region</param> /// <param name="offset">Offset of the memory region</param>
/// <returns>A reference to the given memory region data</returns> /// <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="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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe ref T GetRef<T>(ulong offset) where T : unmanaged public unsafe ref T GetRef<T>(ulong offset) where T : unmanaged
{ {
@ -187,7 +187,7 @@ namespace Ryujinx.Memory
if (endOffset > Size || endOffset < offset) if (endOffset > Size || endOffset < offset)
{ {
ThrowArgumentOutOfRange(); ThrowInvalidMemoryRegionException();
} }
return ref Unsafe.AsRef<T>((void*)PtrAddr(ptr, offset)); 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> /// <param name="size">Size in bytes of the region</param>
/// <returns>The pointer to the memory region</returns> /// <returns>The pointer to the memory region</returns>
/// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> /// <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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public IntPtr GetPointer(ulong offset, int size) => GetPointerInternal(offset, (ulong)size); public IntPtr GetPointer(ulong offset, int size) => GetPointerInternal(offset, (ulong)size);
@ -218,20 +218,20 @@ namespace Ryujinx.Memory
if (endOffset > Size || endOffset < offset) if (endOffset > Size || endOffset < offset)
{ {
ThrowArgumentOutOfRange(); ThrowInvalidMemoryRegionException();
} }
return PtrAddr(ptr, offset); return PtrAddr(ptr, offset);
} }
/// <summary> /// <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> /// </summary>
/// <param name="offset">Start offset of the memory region</param> /// <param name="offset">Start offset of the memory region</param>
/// <param name="size">Size in bytes of the region</param> /// <param name="size">Size in bytes of the region</param>
/// <returns>Span of the memory region</returns> /// <returns>Span of the memory region</returns>
/// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> /// <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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe Span<byte> GetSpan(ulong offset, int size) public unsafe Span<byte> GetSpan(ulong offset, int size)
{ {
@ -239,13 +239,13 @@ namespace Ryujinx.Memory
} }
/// <summary> /// <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> /// </summary>
/// <param name="offset">Start offset of the memory region</param> /// <param name="offset">Start offset of the memory region</param>
/// <param name="size">Size in bytes of the region</param> /// <param name="size">Size in bytes of the region</param>
/// <returns>Memory of the memory region</returns> /// <returns>Memory of the memory region</returns>
/// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception> /// <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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe Memory<byte> GetMemory(ulong offset, int size) 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 ThrowObjectDisposed() => throw new ObjectDisposedException(nameof(MemoryBlock));
private void ThrowArgumentOutOfRange() => throw new ArgumentOutOfRangeException(); private void ThrowInvalidMemoryRegionException() => throw new InvalidMemoryRegionException();
} }
} }