diff options
author | gdkchan <gab.dark.100@gmail.com> | 2020-07-30 10:16:41 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-30 23:16:41 +1000 |
commit | 57bb0abda3dc277dc7575250fdb080edb83abcbc (patch) | |
tree | 21fd7e3cdea856037c4794e2de0b68ff80be4b63 /Ryujinx.Cpu | |
parent | 636542d817b3403ae44b46a48a67cedc0c7b42c5 (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.cs | 9 | ||||
-rw-r--r-- | Ryujinx.Cpu/MemoryManager.cs | 112 |
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)) |