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:
parent
636542d817
commit
57bb0abda3
7 changed files with 143 additions and 75 deletions
|
@ -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));
|
||||||
|
|
||||||
|
|
9
Ryujinx.Cpu/InvalidAccessHandler.cs
Normal file
9
Ryujinx.Cpu/InvalidAccessHandler.cs
Normal 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);
|
||||||
|
}
|
|
@ -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,9 +145,11 @@ namespace Ryujinx.Cpu
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
MarkRegionAsModified(va, (ulong)data.Length);
|
MarkRegionAsModified(va, (ulong)data.Length);
|
||||||
|
|
||||||
if (IsContiguous(va, data.Length))
|
if (IsContiguousAndMapped(va, data.Length))
|
||||||
{
|
{
|
||||||
data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length));
|
data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length));
|
||||||
}
|
}
|
||||||
|
@ -161,6 +178,14 @@ namespace Ryujinx.Cpu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (InvalidMemoryRegionException)
|
||||||
|
{
|
||||||
|
if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a read-only span of data from CPU mapped memory.
|
/// Gets a read-only span of data from CPU mapped memory.
|
||||||
|
@ -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,6 +326,8 @@ namespace Ryujinx.Cpu
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
int offset = 0, size;
|
int offset = 0, size;
|
||||||
|
|
||||||
if ((va & PageMask) != 0)
|
if ((va & PageMask) != 0)
|
||||||
|
@ -317,6 +350,14 @@ namespace Ryujinx.Cpu
|
||||||
_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if a specified virtual memory region has been modified by the CPU since the last call.
|
/// Checks if a specified virtual memory region has been modified by the CPU since the last call.
|
||||||
|
@ -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))
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
19
Ryujinx.Memory/InvalidMemoryRegionException.cs
Normal file
19
Ryujinx.Memory/InvalidMemoryRegionException.cs
Normal 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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue