aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Cpu
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2020-07-30 10:16:41 -0300
committerGitHub <noreply@github.com>2020-07-30 23:16:41 +1000
commit57bb0abda3dc277dc7575250fdb080edb83abcbc (patch)
tree21fd7e3cdea856037c4794e2de0b68ff80be4b63 /Ryujinx.Cpu
parent636542d817b3403ae44b46a48a67cedc0c7b42c5 (diff)
Print guest stack trace on invalid memory access (#1407)
* Print guest stack trace on invalid memory access * Improve XML docs
Diffstat (limited to 'Ryujinx.Cpu')
-rw-r--r--Ryujinx.Cpu/InvalidAccessHandler.cs9
-rw-r--r--Ryujinx.Cpu/MemoryManager.cs112
2 files changed, 86 insertions, 35 deletions
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))