From 57bb0abda3dc277dc7575250fdb080edb83abcbc Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 30 Jul 2020 10:16:41 -0300 Subject: Print guest stack trace on invalid memory access (#1407) * Print guest stack trace on invalid memory access * Improve XML docs --- Ryujinx.Cpu/MemoryManager.cs | 112 +++++++++++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 35 deletions(-) (limited to 'Ryujinx.Cpu/MemoryManager.cs') 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; + + /// + /// Address space width in bits. + /// public int AddressSpaceBits { get; } private readonly ulong _addressSpaceSize; @@ -25,6 +30,9 @@ namespace Ryujinx.Cpu private readonly MemoryBlock _backingMemory; private readonly MemoryBlock _pageTable; + /// + /// Page table base pointer. + /// public IntPtr PageTablePointer => _pageTable.Pointer; /// @@ -32,8 +40,11 @@ namespace Ryujinx.Cpu /// /// Physical backing memory where virtual memory will be mapped to /// Size of the address space - public MemoryManager(MemoryBlock backingMemory, ulong addressSpaceSize) + /// Optional function to handle invalid memory accesses + public MemoryManager(MemoryBlock backingMemory, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler = null) { + _invalidAccessHandler = invalidAccessHandler; + ulong asSize = PageSize; int asBits = PageBits; @@ -92,6 +103,7 @@ namespace Ryujinx.Cpu /// Type of the data being read /// Virtual address of the data in memory /// The data + /// Throw for unhandled invalid or unmapped memory accesses public T Read(ulong va) where T : unmanaged { return MemoryMarshal.Cast(GetSpan(va, Unsafe.SizeOf()))[0]; @@ -102,6 +114,7 @@ namespace Ryujinx.Cpu /// /// Virtual address of the data in memory /// Span to store the data being read into + /// Throw for unhandled invalid or unmapped memory accesses public void Read(ulong va, Span data) { ReadImpl(va, data); @@ -113,6 +126,7 @@ namespace Ryujinx.Cpu /// Type of the data being written /// Virtual address to write the data into /// Data to be written + /// Throw for unhandled invalid or unmapped memory accesses public void Write(ulong va, T value) where T : unmanaged { Write(va, MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref value, 1))); @@ -123,6 +137,7 @@ namespace Ryujinx.Cpu /// /// Virtual address to write the data into /// Data to be written + /// Throw for unhandled invalid or unmapped memory accesses public void Write(ulong va, ReadOnlySpan 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 /// Virtual address of the data /// Size of the data /// A read-only span of the data + /// Throw for unhandled invalid or unmapped memory accesses public ReadOnlySpan GetSpan(ulong va, int size) { if (size == 0) @@ -179,7 +205,7 @@ namespace Ryujinx.Cpu return ReadOnlySpan.Empty; } - if (IsContiguous(va, size)) + if (IsContiguousAndMapped(va, size)) { return _backingMemory.GetSpan(GetPhysicalAddressInternal(va), size); } @@ -204,6 +230,7 @@ namespace Ryujinx.Cpu /// Virtual address of the data /// Size of the data /// A writable region of memory containing the data + /// Throw for unhandled invalid or unmapped memory accesses public WritableRegion GetWritableRegion(ulong va, int size) { if (size == 0) @@ -211,7 +238,7 @@ namespace Ryujinx.Cpu return new WritableRegion(null, va, Memory.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 /// Type of the data to get the reference /// Virtual address of the data /// A reference to the data in memory + /// Throw if the specified memory region is not contiguous in physical memory public ref T GetRef(ulong va) where T : unmanaged { if (!IsContiguous(va, Unsafe.SizeOf())) @@ -256,6 +284,9 @@ namespace Ryujinx.Cpu return ref _backingMemory.GetRef(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)); - 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 /// /// Virtual address to check /// True if the address is mapped, false otherwise + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsMapped(ulong va) { if (!ValidateAddress(va)) -- cgit v1.2.3