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
|
@ -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);
|
||||
|
||||
size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
|
||||
|
||||
data.Slice(0, size).CopyTo(_backingMemory.GetSpan(pa, size));
|
||||
|
||||
offset += size;
|
||||
data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length));
|
||||
}
|
||||
|
||||
for (; offset < data.Length; offset += size)
|
||||
else
|
||||
{
|
||||
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="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>()))
|
||||
|
@ -256,6 +284,9 @@ namespace Ryujinx.Cpu
|
|||
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)]
|
||||
private bool IsContiguous(ulong va, int size)
|
||||
{
|
||||
|
@ -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));
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
for (; offset < data.Length; offset += size)
|
||||
catch (InvalidMemoryRegionException)
|
||||
{
|
||||
ulong pa = GetPhysicalAddressInternal(va + (ulong)offset);
|
||||
|
||||
size = Math.Min(data.Length - offset, PageSize);
|
||||
|
||||
_backingMemory.GetSpan(pa, size).CopyTo(data.Slice(offset, size));
|
||||
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))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue